; ; File: DTDBMgr.a ; ; Contains: Desktop Database manager routines ; ; Written by: Patrick W. Dirks, July 6 1986. ; Rewritten by: David Feldman, April 7, 1989. ; ; Copyright: © 1986-1993 by Apple Computer, Inc., all rights reserved. ; ; Change History (most recent first): ; ; 6/9/93 pdw Changed register saving from interruptRegs to pascalRegs in ; completionRoutine because it is supposed to be using this ; convention. ; 10/27/92 CSS Changed some word bsrs to bigjsrs. ; 10/27/92 CSS Changed short branch to word branch. ; 4/1/92 kc Copied the routines DesktopCloseDownProc, CheckDesktopSupport and DTDBMgrInit ; from FileMgrPatches.a. Removed the "ROM" prefix from the RomBind routines. ; • Pre-SuperMario comments follow • ; <32> 9/13/91 JSM Cleanup header. ; <31> 5/23/91 dba MPW 3.2 assembler no longer supports local labels for records ; <30> 3/28/91 dnf ewa, #SetFileAttribsIsntAsyncSafe: The SetFileAttribs subroutine ; doesn't save its return address on the a6 stack, causing crashes ; when doing async I/O. ; <29> 3/20/91 dnf sad, #DTGetInfoConsumesGranite: Fix DTGetInfo so that the ; AddFileSizes routine that it calls actually works. It is good to ; know that someone is now actually using this call. ; <28> 3/19/91 dnf dba, #dnf: Don’t clobber Z bit when restoring return addresses ; from the a6 stack. ; <27> 3/17/91 dnf ppd, #dnf-0013: Use the result code from ioResult(a0) instead of ; from d0 to be extra-clean on async calls. ; <26> 3/17/91 dnf dba, dty, #84670, #82944: Change more routines to make them work ; in an asynchronous world. The desktop manager now does all of ; its I/O synchronously (except for _FindFolder, a gaping time ; bomb) ; <25> 2/12/91 dnf gbm, #81716: Move the call to DTCloseDown from the Process ; Manager's notification hook to a patch to _Unmount (in ; FileMgrPatches.a) so that it will come after we know that ; nobody on the hook has rejected the unmount. Also put in tweak ; for brc #81858 (replace #fnfErr with #afpItemNotFound) ; <24> 1/22/91 dnf (ppd, tgh) Force DTOpenInform, DTGetPath and DTCloseDown to be ; synchronous. Don’t use MakePBLookAsync anymore. ; <23> 1/17/91 dnf (pwd) Make sure that FindDTFiles has the volume info before it ; tries to decide if the volume is locked or not. ; <22> 12/21/90 dnf (dfh) Move substitution of volume name + shadow for the standard ; name until after we're sure we're not going to create files with ; the standard name. Restore the pb ptr after enqueue the ; DTDBQElt. ; <21> 12/18/90 dnf (dba) Save a hint for each desktop database b-tree file in the ; DTDBQElt, and pass it to all BTree Manager calls. Looks for and ; creates shadow desktop folders in the preferences folder. Also ; rewrote FindDTFiles to be simpler and smaller. Did _Allocate of ; clump size in RealOpenDT and in ResetDT to minimize ; fragmentation. Used HGetFileInfo/HSetFileInfo instead of ; Get/SetCatInfo to be more specific about files vs. directories. ; FlushVol instead of FlushFile. ; <20> 12/16/90 dnf (dba) Fix OpenDTInform bug by getting RealDTOpen to return a ; valid result. Get rid of code that was never executed that made ; a lame attempt to update the key descriptor. ; <19> 12/5/90 dnf (with dba) Have RemoveAPPL look at AppSpec.parID instead of ; ioDirID from the DTParamBlock. Have AddAPPL do this too. ; <18> 12/5/90 dnf (with dba) Fixed three places where we didn’t move the return ; address to the a6 stack before calling potentially async ; routines. Expand the size of a volume name storage to 32 bytes, ; since it is really the root directory name, and is not ; necessarily 28 bytes or smaller. Started work on Preferences ; folder support, but it is still IF’d out for this check-in. ; <17> 9/25/90 dnf (dnf/pwd) Set up FSVars.dtOwnCall to point to each parameter ; block used by the desktop manager so that FileShare can allow ; these calls to pass unmolested. ; <16> 9/22/90 dnf (dnf/pwd) Remove include of QMgr.a; it's now a separate file. ; Dispatch desktop calls to external file systems by transferring ; them to the file system queue and returning an #extFSErr. ; <15> 8/6/90 dnf Make DTAddIcon canonify files before putting them into the ; database. Have DTGetInfo return the volume containing the files ; in ioVRefNum. Revert to using local key compare routine instead ; of a (special) key descriptor. ; <14> 7/30/90 dnf Change all bsr and jsr instructions that point at Rom bind ; symbols into jsrRom macros. ; <13> 6/2/90 gbm Fix Dave’s eensy weensy little mistake of CHECKING IN x xxxx xx ; xxxx’x xxxxxxxxx!!!! (Appropriate slime deleted anyway. ) ; <12> 6/2/90 dnf Make DTDeleteAPPL move the next-newest app to the front of the ; list ; <11> 5/15/90 dnf Make sure that StandardLocals always returns an even-sized ; locals block ; <10> 4/24/90 dnf Fix signed compare bug in GetComment ; <9> 4/22/90 dnf Fix reopening of shadow databases ; <8> 4/20/90 dnf Add creator type check to RemoveAPPL ; <7> 4/13/90 dnf Fix AddAPPL swap case. ; <6> 4/10/90 dnf Clip GetComment ioActCounts. Look in server folder for ; databases. Change routine names to match public call titles ; Rework APPL algorithms to improve speed. ; <5> 2/9/90 DNF Set up a4 on entry to DTSetUp in DTClose; Set ioReqCount with a ; long in RmvAPPL ; <4> 2/8/90 dnf Change FindDTVol to save regs on a7 ; <3> 2/4/90 DNF Add shadow support, implement xOpenDT ; <2> 1/9/90 DNF Add xOpenDT call ; <2.0> 12/8/89 dnf Added dangling DTDBQElt check on OpenDT ; <1.9> 11/17/89 dnf Volume notification for both Offline and Unmount. OpenDT checks ; GetVolParms ; <1.8> 11/10/89 dnf Save all async-trashable regs at I/O bottleneck ; <1.7> 11/8/89 dnf Clear filler byte on IconKeys, set length correctly, add Unmount ; notification ; <1.6> 10/17/89 dnf Fix data rec allocation on APPL calls ; <1.5> 10/11/89 dnf Async bottleneck for I/O, FindDTVol, file refs for DTRefNum, ; <1.4> 9/18/89 dnf Fix bug in GetAPPL, map btRecNotFnd to afpItemNotFnd, new ; dispatcher ; <1.3> 9/1/89 dnf Get proper pointer in a1 for DTClose's call to _Dequeue, test ; the result ; <1.2> 8/7/89 dnf Change IconRec to IconDRec to avoid collisions with QD equates ; <1.1> 7/6/89 dnf Complete alpha code, start of testing ; <1.0> 5/30/89 dnf Integrate CatSearch, FileID's and Desktop Database Mgr into one ; ptch ; 4/7/89 DNF Rewritten to use newly exported Btree manager calls, new DT data ; structures, new DT dispatching, and new external file system ; handling. This rewrite uses almost all of the existing logic for ; the service routines. ; 5/23/88 PWD Changed AddAPPL to use DTContd flag logic for complete cycle ; (FindRecord as well as subsequent GetRecords); the entire ; AddAPPL now unfolds into a true loop instead of burrowing its ; way down the stack with successive serial number retries. ; 5/3/88 PWD Added code to set up DTORefNum and DTVRefNum at open time; fixed ; MapCSpec to use new DTORefNum to do _GetCatInfo, fixing a bug ; where comment calls on CD ROM volumes would try the mapping with ; the wrong VRefNum. ; 4/25/88 PWD Changed all interrupt-lockout code to use $0600 instead of $0300 ; or HiIntMask. ; 3/1/88 PWD Changed to free reserved space at first DTOpen call made. ; 10/23/87 PWD Fixed AddIcon to rewrite B*-Tree entry with new TagInfo if an ; icon is overwritten with a new icon image. ; 10/8/87 PWD Changed to use common routine (FindDT) to check whether DB ; exists in DTOpen and GtMetaInfo. For CD-ROM volumes, only ; indicate DB support if the Desktop DB file already exists. ; 9/16/87 PWD Changed to set Type & Creator on Desktop datafile. ; 8/13/87 PWD Changed to use shadow-Desktop in System Folder of BootDrive if ; volume is locked and no Desktop DB exists. ; 7/27/87 CRZ Changed some branches to short branches and vice versa. ; 6/26/87 PWD Changed version number from 1 to 2 to indicate DTClose bug ; fixed, FSQueueHook support in. ; 6/17/87 PWD Fixed bug in DTClose where it RTS-ed instead of branching to ; DTDone when DTSetup returned VolOfflinErr. Changed to use AFP ; error message names. Added code to invoke FSQueueHook on all ; calls. ; 1/6/87 PWD Changed DTFlags to DTFFlags to avoid conflict with Deferred Task ; Manager equate ; 1/2/87 PWD Changed to retry call if Server Folder ID is set in Finder info ; but no such folder is found. ; 12/8/86 PWD Changed to skip completion routines called synchronously to save ; stack space. ; 12/1/86 PWD Changed to clump out data file by explicit _Allocate calls ; before an icon is written out instead of modifying FCBClpSize. ; 11/20/86 PWD Changed to clear hint before re-inserting entry after BTDelete ; in InsEntry. Changed key comparison to ignore length of trial ; key (padded out in index nodes). Changed all routines to pass ; full-length keys (Getxxx would get fouled up by passing ; partial-length keys to new key comparison routine. ; 11/17/86 PWD Fixed GetMetaInfo call to return right bit for DT support ; Changed to check refNum in detail only for local operation ; 11/13/86 PWD Fixed dispatch logic to properly preserve A1 after DTDone ; 11/12/86 PWD Fixed async return code. Always RTS after async I/O initiation ; Incorporated various small modifications per Rich's suggestions ; Changed to use GetTrapAddress/SetTrapAddress on install and ; remove. ; 11/2/86 PWD Changed to support only drives 3 and up to prevent the ; proliferation of Desktop databases on diskettes. Changed DTOpen ; to set invisible and system (name lock) bits on both files. ; Changed AddAPPL to stop re-writing B*-Tree records if a ; duplicate entry is requested with the same tag. Added DTReset ; and DTStatus calls. Changed GetIcon and GtIcnInfo to return icon ; position in ioPosOffset. Changed GetIcon to take a hint when ; given. Changed serial number assignment algorithm in AddAPPL to ; return last entries added first. ; 10/28/86 PWD Fixed GetIcon to handle _Read errors correctly. ; 10/23/86 PWD Converted for use under MPW. ; 10/15/86 PWD Changed GetIcon to stop using ioIconDefault field. Changed to ; return new meta-info indicating new DT support present. ; 10/13/86 PWD Added code to set up ReqstVol on ExtFSErr returns. ; 10/12/86 PWD Fixed similar bugs in AddAPPL and RmvAPPL which caused name ; comparisons between the B*-Tree record and PB Record to fail. ; Fixed bug in RfnChk (failed to set condition codes on exit). ; Added code to force clump size for Desktop Datafile. ; 10/10/86 PWD Fixed to check for offline volumes before starting any ; operation. Fixed bug which clobbered lo-mem due to bogus ; ioOwnBuf on _HOpen. Added code to do a BTFlush. ; 10/6/86 PWD Fixed to locate target VCB correctly under all circumstances ; Changed DTGetAPPL to special case index=0; treat like index=1 ; Fixed calls to MapCSpec to handle errors correctly. ; 10/4/86 PWD Fixed to properly set up ioTrap field on all calls. ; 10/2/86 PWD Fixed bug in DTOpen: don't use user's PB for _HOpen call. ; Changed to repond to GetVolMetaInfo calls to indicate new ; desktop support. ; 9/24/86 PWD Changed to use new key structure ; 8/21/86 PWD Changed to remove patch installation code and local storage ; 8/5/86 PWD Added code to convert internal B*-Tree error codes to AFP ; errors, and filter out other positive error returns ; 7/17/86 PWD Separated out from DTMgr to allow DTSvcs to be included in patch ; code (where it's in blank seg) and FSCP, where it sits with the ; B*-Tree manager in the BTMgr segment. ; print push print off LOAD 'StandardEqu.d' INCLUDE 'BTreeEqu.a' INCLUDE 'DTDBMgrEqu.a' INCLUDE 'DTDBMgrPriv.a' include 'LinkedPatchMacros.a' include 'QMgrEqu.a' include 'FileMgrPrivate.a' include 'MFPrivate.a' include 'HardwarePrivateEqu.a' include 'Folders.a' include 'InternalOnlyEqu.a' ; CSS print pop BLANKS ON STRING ASIS entry DequeueDTDBQElt entry deepShitError import GetQMRecPtr macro MakeLocals endm ; All of the routines in the desktop manager that have a parameter block as one of their ; locals have one of size DTBigPBSize, and it is always the first local on a6. ; All of the service routines of the DTMgr use a very similar local variable layout ; The only difference among the routines is the size of the Data block they need. ; This macro takes a label name (for the locals record) and a size for the data block ; in that record. macro StandardLocals &name, &datalen &name record 0, increment bigPB ds.b DTBigPBSize ; big enough for any call made from the DT routines Key ds.b DTMaxKeySize ; enough space to form any DT key align 2 ; max key length could be odd Data ds.b &datalen ; enough space for a data record of requested length align 2 ; data size could be odd LSize equ *-bigPB endr endm ; The key descriptor we use for our BTrees simply tells the btree manager that we'll ; always be using our own key descriptor routine. DTKeyDescriptor: proc dc.b 2 ; length of descriptor dc.b kdUseKCProc ; we'll always be using a compare proc dc.b 1 ; just one of them align 2 ; line things up endproc ; so desktop symbols start from here proc _DTDebugRts 'DTDBQueueManager', 0 endproc ;________________________________________________________________________________ ; <36> ; Routine: DesktopCloseDownProc ; ; Inputs param block to _Unmount ; ; Outputs: (none) ; ; Function: Close the desktop database on _Unmount calls ; ;________________________________________________________________________________ ; Rolled into TFS.a for SuperMario FM ; Called by UnMountVol in TFSVol.a DesktopCloseDownProc Proc Export import FindDTVol @regs reg a0/a1/a3/d0/d1 ; <43> movem.l @regs, -(sp) movea.l a0,a3 ; save the user's pb <42> ; d0 - pascal length byte of string <43> ; d1 - size of string allocated on stack <43> moveq.l #0,d1 ; assume no string on stack <43> move.l ioNamePtr(a3),d0 ; d0 = caller’s name ptr <43> beq.s @noName ; bail on nil <43> movea.l d0,a0 ; get ready to copy <43> moveq.l #0,d0 ; clear high bytes <43> move.b (a0),d0 ; d0 = string length <43> beq.s @noName ; bail on zero length (d0.l has nil) <43> move.b d0,d1 ; d1 = copy of string length byte <43> addq.b #2,d1 ; add length byte and rounding fodder <43> bclr.l #0,d1 ; make it even <43> suba.w d1,sp ; allocate a string of the right length <43> movea.l sp,a1 ; point to it <43> @1: move.b (a0)+,(a1)+ ; <43> dbra d0,@1 ; copy string length+1 bytes <43> move.l sp,d0 ; point to it again <43> @noName: suba.w #ioHVQElSize,sp ; get a pb <42> movea.l sp,a0 ; point to it <42> move.w ioVRefNum(a3),ioVRefNum(a0) ; copy caller's vRefNum <42> move.l d0,ioNamePtr(a0) ; our version of the caller's name <42> move.w #-1,ioVolIndex(a0) ; by name&vRef, please <42> _GetVolInfo ; <42> bne.s @done ; <42> move.w ioVRefNum(a0), d0 ; grab the volume that's going away bsr FindDTVol ; try to find a DTDBQElt for this volume bne.s @done ; no work to do if the DTDB is closed sub.w #ioDTQElSize, sp ; allocate a DT param block movea.l sp, a0 move.w DTDBQElt.DTRefNum(a3), ioRefNum(a0) ; stash the DTRefNum for this volume _DTCloseDown add.w #ioDTQElSize, sp ; deallocate the param block @done: adda.w #ioHVQElSize,sp ; deallocate the pb <42> adda.w d1,sp ; deallocate the string <43> movem.l (sp)+, @regs rts endproc ;__________________________________________________________________________________ ; ; Allocate the DTDBMgr's globals block ; ; FSVars has been allocated by btree patches. ;__________________________________________________________________________________ DTDBMgrInit proc export export QMInit move.l #DTGlobals.size, d0 ; we need this much space for DT manager _NewPtr sys, clear bne @fail ; no? Run away. move.l FSVarsPtr, a1 ; a1 = ptr(FSVars block) move.l a0, FSVars.DTDBMgr(a1) ; stuff ourselves into our slot in FSVars rts @fail moveq.l #dsMemFullErr, d0 ; sys heap is full, so punt _SysError ; Allocate the Queue Manager's globals block QMInit ; Allocate a block big enough of all of the Queue Manager's needs: ; Queue manager globals ; Desktop manager QMRec ; Compatibility layer QMRec ; Desktop manager stack ; Compatibility layer stack move.l #QMGlobals.size+QMRec.size*2+DTStackSize+clStackSize, d0 _NewPtr sys, clear bne.s @fail ; fail if we can't get memory move.l FSVarsPtr, a1 ; a1 = ptr(FSVars block) move.l a0, FSVars.QMgr(a1) ; leave a pointer to globals in FSVars move.l a0, a1 ; a1 = ptr(FSVars.QMgr) adda.l #QMGlobals.size, a0 ; skip over globals move.l a0, QMGlobals.DTQueuePtr(a1); stuff pointer to DTMgr's QMRec adda.l #QMRec.size, a0 ; skip over DTMgr's QMRec move.l a0, QMGlobals.CLQueuePtr(a1); stuff pointer to Comp Layer's QMRec adda.l #QMRec.size+DTStackSize, a0 ; skip over QMRec and Stack move.l QMGlobals.DTQueuePtr(a1), a1 ; a1 = ptr(DTMgr's QMRec) move.l a0, QMRec.stackBase(a1) ; store stack address (stack grows down) move.w #desktopQType, QMRec.qType(a1) ; set queue type/refnum adda.l #CLStackSize, a0 ; skip to end of CLStack move.l FSVarsPtr, a1 ; a1 = ptr(FSVars) move.l FSVars.QMgr(a1), a1 ; a1 = ptr(QM globals) move.l QMGlobals.CLQueuePtr(a1), a1 ; a1 = ptr(Comp Layer's QMRec) move.l a0, QMRec.stackBase(a1) ; store stack address (stack grows down) move.w #fsCompatQType, QMRec.qType(a1) ; set queue type/refnum rts @fail: moveq.l #dsMemFullErr, d0 ; sys heap is full, so punt _SysError endproc ; FM needed for GetVolParms ;________________________________________________________________________________ ; ; Routine: CheckDesktopSupport ; ; Input: a2 - pointer to local volume's vcb ; Output: zero flag clear if we support the desktop on this volume ; zero flag set if we don't ; ; Function: Indicate whether a volume can support the desktop manager calls ; ; Only call this on local hfs volumes ; ; The rule: A local volume gets desktop support if it is either ; 1) non-ejectable ; 2) ejectable but bigger than DTMinVolSize ; ; AlkBlkSiz is stored as a long and used as a word throughout HFS ;________________________________________________________________________________ CDSRegs reg d0/d1/a1 CheckDesktopSupport proc export movem.l CDSRegs, -(sp) ; ; Is this an MFS volume? ; cmp.w #SigWord,vcbSigWord(a2) ; is this an MFS volume? beq.s @NoSupport ; yes, so use the exciting resource file technique ; ; Is this volume bigger than DTMinVolSize? ; move.w vcbNmBlks(a2), d0 ; d0 = # of allocation blocks on this volume move.w vcbAlBlkSiz+2(a2), d1 ; d1 = allocation block size ; AlkBlkSiz is used as a word throughout HFS mulu.w d1, d0 ; d0 = # bytes on this volume cmp.l #DTMinVolSize, d0 ; is this a big volume? bhs.s @SupportsDT ; then we always support the DTDB ; ; It's not a big volume, but check and see if the volume is ejectable ; move.l DrvQHdr+qHead, a1 ; a1 = ptr (1st drive in Drive Queue) move.w vcbDrvNum(a2), d0 ; d0 = drive number for this volume (if online) bne.s @SearchDrvQ ; if it's online, we can go straight to the search move.w vcbDRefNum(a2), d1 ; d1 = + or - DrvNum (since we know we're not online) cmp.w d0, d1 ; = means volume is ejected beq.s @NoSupport ; which means it gets no DT support move.w d1, d0 ; d0 now = -DrvNum (since we know it's offline) neg.w d0 ; d0 now = DrvNum, so we can search the drive Q ; ; We now have the drive number in d0, and we know that the drive is not big enough to ; automatically get DT support. We need to see if it is ejectable, and if it is a small ; non-ejectable volume (say, a ramdisk) we support it. ; @SearchDrvQ: cmp.w dQDrive(a1), d0 ; is this the dQ entry we want? beq.s @GotDQ move.l qLink(a1), d1 ; get the next dQ entry (test for zero) movea.l d1, a1 ; move to an A reg where we can use it bne.s @SearchDrvQ ; and keep going if there is one move.w #nsvErr, d0 ; if we have a real vcb, we should find a dQ entry bra.s @NoSupport ; but if we ever don't, let's return 'no support' @GotDQ: cmp.b #8, -3(a1) ; drive queue entry flag bytes => 8 (signed) are non-ejectable <19><51> blt.s @NoSupport ; we don't support DT on small ejectable volumes @SupportsDT: moveq.l #1, d0 ; clear the zero flag bra.s @CDSExit @NoSupport: moveq.l #0, d0 ; set the zero flag @CDSExit: movem.l (sp)+, CDSRegs rts endproc ;________________________________________________________________________________ ; ; Routine: BottleNeckIO ; ; Input: a0 points to dt's ioParam or BTioParam ; Output: d0 contains result code ; a1 trash ; ; Function: Make traps which do I/O and do the right thing with completion ; routines. ;________________________________________________________________________________ ; BottleNeckRegs reg d1-d7/a1-a5 ; async could trash any regs BottleNeckLocals record 0, increment hasContinued ds.b 1 ; flag byte to watch stack depth align 2 Lsize equ *-BottleNeckLocals endr BottleNeckIO proc export movem.l BottleNeckRegs, -(a6) ; save our regs on hfs stack with BottleNeckLocals subq.w #Lsize, a6 bclr.b #0, hasContinued(a6) ; initialize completion routine flag lea.l completionRoutine, a1 ; get our ioCompletion routine address move.l a1, ioCompletion(a0) ; and install it into the param block movea.l FSVarsPtr, a1 ; <17> move.l a0, FSVars.dtOwnCall(a1); leave a bread crumb for FileShare <17> moveq.l #desktopQType, d2 ; our queue type/refnum move.l a4,-(sp) ; save a4 CSS bigjsr GetQMRecPtr,a4 ; a1 = ptr(QMRec) CSS move.l (sp)+,a4 ; recover a4 CSS move.l a6, QMRec.curStack(a1) ; save current alt stack pointer ; •• do high water mark checking here btst.b #asyncCall, QMRec.qmFlags(a1) ; is this call async? rts ; return to specific trap handling routine _DTDebugTail 'TheBottleNeck', 0 ; Some drivers have the nasty habit of calling their completion routines before returning ; from the trap that called them. This might lead to unbounded stack buildup from ; successive calls. To solve this we have a bit (in hasContinued) which is set both ; in the completion routine and after the trap. ; Case 1: The completion routine is called before we return from the trap. In this case ; we just cruise back to the driver, since we'll get control again when the driver ; rts's from our trap. When that happens we can just continue our call. ; Case 2: We get returned to before the completion routine is run (truly asynchronous). ; We rts to the app (giving back the async time) and we know we'll get control again ; at the completion routine. When we continue from here we need to save the ; appropriate interrupt registers. ; This little scheme is, of course, a critical section, but we're single threaded right now, ; so it doesn't matter. completionRoutine: moveq.l #desktopQType, d2 ; our queue type/refnum MACHINE mc68020 move.l a4,-(sp) ; save a4 CSS bigjsr GetQMRecPtr,a4 ; a1 = ptr(QMRec) CSS move.l (sp)+,a4 ; recover a4 CSS move.l a6, -(sp) ; save a6 for a sec movea.l QMRec.curStack(a1), a6 ; get our a6 back bset.b #0, hasContinued(a6) ; mark that we've been back movea.l (sp)+, a6 ; restore a6 beq.s anRTSInstruction ; if we haven't returned from the trap, rts to driver movem.l pascalRegs, -(sp) ; save all regs that pascal callers need saved pdw pea restorePascalRegs ; and get in the chain to restore them later pdw contDeskThread: moveq.l #desktopQType, d2 ; our queue type/refnum MACHINE mc68020 move.l a4,-(sp) ; save a4 CSS bigjsr GetQMRecPtr,a4 ; a1 = ptr(QMRec) CSS move.l (sp)+,a4 ; recover a4 CSS movea.l QMRec.curStack(a1), a6 ; restore alt stack pointer adda.w #Lsize, a6 ; clear off the locals movem.l (a6)+, BottleNeckRegs ; restore desktop thread registers move.l (a6)+, -(sp) ; push desktop thread return address move.w ioResult(a0),d0 ; get the real error code and set ccr's <27> anRTSInstruction: rts ; and return to caller contAppThread: bset.b #0, hasContinued(a6) ; mark that we've returned bne.s contDeskThread ; if we already ran the completion routine, then ; continue without saving registers rts ; return async time to application restorePascalRegs: ; pdw movem.l (sp)+, pascalRegs ; restore the regs that we saved last time through rts ; back to the app thread _DTDebugTail 'Completion',0 ; Macro for generating code that executes a synchronous or asynchronous trap ; based on the value of the zero flag. Zero set = async, zero clear = sync. macro &sym: doSomeTrap &theTrap entry &sym &sym: move.l (sp)+, -(a6) ; save desktop thread ret addr on a6 bsr.s BottleNeckIO ; do common set up bne.s @async ; branch if Z = 0 (async call) _&theTrap ; sync trap bra contDeskThread ; keep going with this desktop call @async: _&theTrap async ; async trap bra contAppThread ; keep going, but give async time to app _DTDebugRts 'do&theTrap',0 endm ; Macro for generating code that executes a synchronous or asynchronous hfsdispatch ; based on the value of the zero flag. Zero set = async, zero clear = sync. macro &sym: doSomeA060Trap &theTrap, &theDispatch entry &sym &sym: move.l (sp)+, -(a6) ; save desktop thread ret addr on a6 bsr.s BottleNeckIO ; do common set up bne.s @async ; branch if Z = 0 (async call) moveq.l #&theDispatch, d0 ; tell'em which HFS dispatch to do _HFSDispatch ; sync trap bra contDeskThread ; keep going with this desktop call @async: moveq.l #&theDispatch, d0 ; tell'em which HFS dispatch to do _HFSDispatch async ; async trap bra contAppThread ; keep going, but give async time to app _DTDebugRts 'do&theTrap',0 endm ; Macro for generating code that executes a synchronous or asynchronous hfsdispatch ; based on the value of the zero flag. Zero set = async, zero clear = sync. macro &sym: doSomeA08ETrap &theTrap, &theDispatch entry &sym &sym: move.l (sp)+, -(a6) ; save desktop thread ret addr on a6 bsr.s BottleNeckIO ; do common set up bne.s @async ; branch if Z = 0 (async call) moveq.l #&theDispatch, d0 ; tell'em which HFS dispatch to do dc.w $A08E ; sync trap bra contDeskThread ; keep going with this desktop call @async: moveq.l #&theDispatch, d0 ; tell'em which HFS dispatch to do dc.w $A48E ; async trap bra contAppThread ; keep going, but give async time to app _DTDebugRts 'do&theTrap',0 endm doHOpen: doSomeTrap HOpen doHDelete: doSomeTrap HDelete doClose: doSomeTrap Close doRead doSomeTrap Read doWrite: doSomeTrap Write doGetCatInfo: doSomeA060Trap GetCatInfo, selectGetCatInfo doHGetVInfo: doSomeTrap HGetVInfo doHGetFileInfo: doSomeTrap HGetFileInfo doHSetFileInfo: doSomeTrap HSetFileInfo doHCreate: doSomeTrap HCreate doAllocate: doSomeTrap Allocate doSetEOF: doSomeTrap SetEOF doGetEOF: doSomeTrap GetEOF doGetFCBInfo: doSomeA060Trap GetFCBInfo, selectGetFCBInfo doFlushVol: doSomeTrap FlushVol doMakeFSSpec: doSomeA060Trap MakeFSSpec, selectMakeFSSpec doGetVolParms: doSomeA060Trap GetVolParms, selectGetVolParms doBTSearch: doSomeA08ETrap BTSearch, 6 doBTGetRec: doSomeA08ETrap BTGetRec, 7 doBTSetRec: doSomeA08ETrap BTSetRec, 4 doBTReplRec: doSomeA08ETrap BTReplRec, 5 doBTDelRec: doSomeA08ETrap BTDelRec, 8 doBTFlush: doSomeA08ETrap BTFlush, 10 endproc ; ; A little wrapper for Begin/EndSystemMode to save regs including CCR ; myBeginSystemMode: proc myBeginSystemModeRegs reg d0-d2/a0/a1 move.w sr,-(sp) movem.l myBeginSystemModeRegs, -(sp) ; pascal call trashes these suba.w #2, sp ; space for output _BeginSystemMode adda.w #2, sp ; ignore the error movem.l (sp)+, myBeginSystemModeRegs rtr _DTDebugTail 'myBeginSystemMode',0 endproc myEndSystemMode: proc myEndSystemModeRegs reg a0/a1/d0-d2 move.w sr,-(sp) movem.l myEndSystemModeRegs, -(sp) ; pascal call trashes these suba.w #2, sp ; space for output _EndSystemMode adda.w #2, sp ; ignore the error movem.l (sp)+, myEndSystemModeRegs rtr _DTDebugTail 'myEndSystemMode',0 endproc ;________________________________________________________________________________ ; ; Routine: DeskMgrQueue ; ; input: user's DTparam in a0 ; output: a1 - trashed ; ; Function: We're always called here while on the file system queue (upon which we ; checked for calls to external file systems). We first extricate ourselves ; from the file system queue and then enqueue ourselves on the desktop manager ; queue, setting up our own alternate (a6) stack and leaving all set to ; process the call. ; ; Note that we jump to QMEnqueue so that we leave only one level of ; return address on top of the stack. ;________________________________________________________________________________ ; entry DTDone DeskMgrQueue: proc import UnMungeTrapWord, MakePBLookAsync, GetOffFSQueue import QMEnqueue jsr GetOffFSQueue ; Get a0 (now FSQHead) off FSQueue moveq.l #desktopQType, d2 ; the dtqueue refnum ; bsr MakePBLookAsync ; adjust for the fact that we could be async now jsr UnMungeTrapWord ; leave true trap word and dispatch in d1/d0 jmp QMEnqueue ; get queued up, dispatched to. CmdDone addr on a6 stack _DTDebugRts 'DeskMgrQueue',0 endp DTDone: proc import UnMungeTrapWord, FSQUEUE , CMDDONE move.l (a6)+, a1 ; get completion address from a6 stack ; error mapping - the original implementation of the desktop manager returned afp error ; codes, so we need to continue to do so. @MapErrors: cmp.w #btRecNotFnd, d0 ; did we get a btRecNotFnd error? bne.s @notBtRecNotFnd ; no - go check the next error move.w #afpItemNotFound, d0 ; yes - then replace it with the right afp error bra.s @justLeave @notBtRecNotFnd: cmp.w #btEofErr, d0 ; did we get run into the end of the btree file? bne.s @notBtEofErr ; no - go check the next error move.w #afpItemNotFound, d0 ; yes - tell'em about it in afp style bra.s @justLeave @notBtEofErr: cmp.w #fnfErr, d0 ; #fnfErr is an HFS error code <25> bne.s @notFnfErr ; no - go check the next thing <25> move.w #afpItemNotFound, d0 ; be true to our afp roots <25> @notFnfErr: @justLeave: jmp (a1) _DTDebugRts 'DTDone', 4 endp ;________________________________________________________________________________ ; ; Routine: DTVolExtFSCheck ; ; Function: Check whether this desktop call should be routed to an external ; file system. We're always called here already on the file system ; queue. That gives us the right to peek around lomem looking for ; our volume. If the call turns out to be for an external file system ; we strip one return address (the one back to the DTMgr routine which ; called us), set ReqstVol and return an #extFSErr to CMDDONE , which ; dutifully dispatches it down the toExtFS chain. ; ; If the call turns out to be local, we rts to the DTMgr routine which ; called us. The DTMgr routine will then call DTMgrQueue, which knows ; the the param block it is attempting to enqueue on the desktop manager ; queue needs to be removed from the file system queue first. ; ; Inputs: a0 - caller's desktop manager param block ; ;________________________________________________________________________________ DTVolExtFSCheck: proc jsrROM DTRMV3 ; Excuse me, Mr. FS, please find us a volume bne.s @LocalErr ; early trouble is a sign to leave tst.w vcbFSID(a2) ; is this a local volume? beq.s @LocalVolume move.l a2, ReqstVol ; inform external file systems of our volume choice move.b ioTrap+1(a0), d0 ; grab our dispatch selector cmp.b #selectDTGetPath, d0 ; was it DTGetPath? beq.s @GrabAnFCB ; externals expect an FCB on a DTOpen cmp.b #selectDTOpenInform, d0 ; was it DTOpenInform? bne.s @ExternalExit ; if not, just let'em have the call @GrabAnFCB: jsrROM GT1STFCB ; get (A1,D1) pointing to first FCB @1 tst.l FCBFlNm(a1,d1) ; FCB unused? beq.s @GotFCB ; br if so jsrROM GTNXTFCB ; get next one until we run out bcs.s @1 moveq.l #TMFOErr,d0 ; too many files open clr.w IORefNum(a0) ; make refnum invalid bra.s @LocalErr ; don't bother to pass this one along @GotFCB: add.l a1,d1 ; Point to the FCB @ExternalExit: moveq.l #extFSErr, d0 ; tell'em we can't handle it @LocalErr: tst.l (sp)+ ; discard the return address to DTMgr caller jmpROM CMDDONE ; leave through the file system @LocalVolume: move.l FSVarsPtr, a3 ; a3 = ptr(file system global area) move.l FSVars.DTDBMgr(a3), a3 ; a3 = ptr(DTDB manager's global area) move.w vcbVRefNum(a2), DTGlobals.targetVRef(a3) ; save our volume for later rts ; this call is for us endproc ;________________________________________________________________________________ ; ; Routine: DTRfnExtFSCheck ; ; Function: Check whether this desktop call should be routed to an external ; file system. We're always called here already on the file system ; queue. That gives us the right to peek around lomem looking for ; our volume. If the call turns out to be for an external file system ; we strip one return address (the one back to the DTMgr routine which ; called us), set ReqstVol and return an #extFSErr to CMDDONE , which ; dutifully dispatches it down the toExtFS chain. ; ; If the call turns out to be local, we rts to the DTMgr routine which ; called us. The DTMgr routine will then call DTMgrQueue, which knows ; the the param block it is attempting to enqueue on the desktop manager ; queue needs to be removed from the file system queue first. ; ; Inputs: a0 - caller's desktop manager param block ; ;________________________________________________________________________________ DTRfnExtFSCheck: proc import RefNumCheck move.w ioRefNum(a0), d0 ; get the refnum in question move.w d0, d1 ; save a copy for later jsr RefNumCheck ; see if we like this refnum bne.s @LocalErr ; early trouble is a sign to leave movea.l FCBSPtr, a1 ; point to the FCBs movea.l fcbVPtr(a1, d1.w), a2 ; get the VCB in question tst.w vcbFSID(a2) ; is this a local volume? beq.s @LocalVolume move.l a2, ReqstVol ; inform external file systems of our volume choice moveq.l #extFSErr, d0 ; tell'em we can't handle it @LocalErr: tst.l (sp)+ ; discard the return address to DTMgr caller jmpROM CMDDONE ; leave through the file system @LocalVolume: rts ; this call is for us endproc ;________________________________________________________________________________ ; <21> ; Routine: SaveHintInDTDBQElt param block -> dtqelt ; ; Function: Gets the btree hint out of the current param block and ; stores it in the globabls for the database that the ; current call is running on. ; ; Inputs: a0 - btioParam block ; a3 - pointer to current DTDBQElt ; ; Outputs: a0 - pointer to BTParam ; a3 - pointer to current DTDBQElt ; also preserves CCR ; ;________________________________________________________________________________ SaveHintInDTDBQElt proc Registers reg a1/a2 movem.l Registers,-(sp) lea.l DTDBQElt.lastHint(a3), a1 ; a1 = ptr(recent bt hint in DTDBQElt) lea.l ioBTHint(a0), a2 ; a2 = ptr(bt hint in param block) move.l (a2)+,(a1)+ ; a hint is 16 bytes move.l (a2)+,(a1)+ move.l (a2)+,(a1)+ move.l (a2)+,(a1)+ movem.l (sp)+,Registers rts endproc ;________________________________________________________________________________ ; <21> ; Routine: GetHintFromDTDBQElt dtqelt -> param block ; ; Function: Gets the btree hint out of the the globabls for the database ; that the current call is running on and stores it into the ; btioParam on a0. ; ; Inputs: a0 - btioParam block ; a3 - pointer to current DTDBQElt ; ; Outputs: a0 - pointer to BTParam ; a3 - pointer to current DTDBQElt ; ;________________________________________________________________________________ GetHintFromDTDBQElt proc Registers reg a1/a2 movem.l Registers,-(sp) lea.l DTDBQElt.lastHint(a3),a1 ; a1 = ptr(recent bt hint in DTDBQElt) lea.l ioBTHint(a0),a2 ; a2 = ptr(bt hint in param block) move.l (a1)+,(a2)+ ; a hint is 16 bytes move.l (a1)+,(a2)+ move.l (a1)+,(a2)+ move.l (a1)+,(a2)+ movem.l (sp)+,Registers rts endproc ;________________________________________________________________________________ ; ; Routine: SetUpBTPB ; ; Function: Gets a param block on a0, and then fills in BTRefNum, key and data pointers ; based on the standard locals layout. ; ; Inputs: a3 - pointer to current DTDBQElt ; a6 - pointer to executing routine's locals ; ; Outputs: a0 - pointer to BTParam ; a2 - pointer to space to build the BTree key ; a3 - pointer to current DTDBQElt ; ;________________________________________________________________________________ SetUpBTPB: proc StandardLocals BTDummy, 2 ; just to get key and data offsets with BTDummy ; Key and Data refer to locals lea.l bigPB(a6), a0 ; a0 = ptr(param block) bsr GetHintFromDTDBQElt ; get the hint <21> move.w DTDBQElt.DBRefNum(a3), ioRefNum(a0) ; use the btree refnum lea.l Data(a6), a2 ; standard location of BTree record data buffer move.l a2, ioBuffer(a0) ; tell the param block about it lea.l Key(a6), a2 ; a2 = ptr(place to build key) move.l a2, ioBTKeyPtr(a0) ; tell the param block about it rts endwith _DTDebugTail 'SetUpBTPB',0 endproc ;________________________________________________________________________________ ; <21> ; Routine: DoneBTPB ; ; Function: Gets the btree hint out of the current param block and ; stores it in the globabls for the database that the ; current call is running on. ; ; Inputs: a3 - pointer to current DTDBQElt ; a6 - pointer to executing routine's locals ; also preserves CCR ;________________________________________________________________________________ DoneBTPB proc StandardLocals BTDummy, 2 ; just to get key and data offsets with BTDummy ; Key and Data refer to locals move.w sr,-(sp) ; save CCR move.l a0,-(sp) lea.l bigPB(a6), a0 ; a0 = ptr(param block) bsr SaveHintInDTDBQElt move.l (sp)+,a0 rtr ; restore CCR endwith _DTDebugTail 'DoneBTPB',0 endproc ;________________________________________________________________________________ ; ; Routine: SetUpDFPB ; ; Function: Gets a param block on a0, and then fills in DFRefNum ; ; Inputs: a3 - pointer to current DTDBQElt ; a6 - pointer to current StandardLocals ; ; Outputs: a0 - pointer to param block ; ;________________________________________________________________________________ SetUpDFPB: proc StandardLocals DFDummy, 0 ; just to get offsets with DFDummy lea.l bigPB(a6), a0 ; a0 = ptr(param block) move.w DTDBQElt.DFRefNum(a3), ioRefNum(a0) ; use icon data file refNum rts _DTDebugTail 'SetUpDFPB',0 endwith endproc ;________________________________________________________________________________ ; ; Routine: FindDTVol ; ; Function: See if a desktop database is open for a given VRefNum ; ; Inputs: d0 = user's VRefNum ; ; Output: d0 = noErr or rfNumErr ; a3 = Pointer to DTDBQElt block for selected database ; ; All other registers preserved. ; Note that the DesktopCloseDownProc in FileMgrPatches.a calls this routine. ;________________________________________________________________________________ ; FindDTVol: proc export ; <25> added the export move.l d1, -(sp) ; save d1 around call move.l FSVarsPtr, a3 ; a3 = ptr(file system global area) move.l FSVars.DTDBMgr(a3), a3 ; a3 = ptr(DTDB manager's global area) move.l DTGlobals.qHead(a3), a3 ; a3 = ptr(1st open database) bra.s @nextPtr ; check for nil (no open databases) @WalkQueue: cmp.w DTDBQElt.XVRefNum(a3), d0 ; is this the right volume? beq.s @foundIt ; yes? Then stop looking move.l DTDBQElt.qLink(a3), a3 ; move on to the next one @nextPtr: move.l a3, d1 ; set condition codes to check for nil bne.s @WalkQueue ; something there? keep looking. ; If we get here, then we saw a nil pointer, which means none of the open databases ; matched the user's volume. Return a rfNumErr move.w #rfNumErr, d0 ; set result code bra.s @leave @foundIt: move.w #0, d0 ; no errors @leave: move.l (sp)+, d1 ; restore d1 tst.w d0 ; set the result code rts _DTDebugTail 'FindDTVol',0 endproc ;________________________________________________________________________________ ; ; Routine: DTSetup ; ; Function: Setup common to all desktop manager operations which take a DTRefNum. ; ; Inputs: a4 = DT Parameter block ; a6 = alt stack ; Output: d0 = error code (from FindDTVars). ; a3 = Pointer to DTQElt block (if DTRef checked out) ; a6 = alt stack ; ; All other registers preserved. ;________________________________________________________________________________ ; DTSetup: proc move.l d1, -(a6) ; save d1 around call move.w ioDTRefNum(a4), d0 ; extract DTRefNum from user's param block move.l FSVarsPtr, a3 ; a3 = ptr(file system global area) move.l FSVars.DTDBMgr(a3), a3 ; a3 = ptr(DTDB manager's global area) move.l DTGlobals.qHead(a3), a3 ; a3 = ptr(1st open database) bra.s @nextPtr ; check for nil (no open databases) @WalkQueue: cmp.w DTDBQElt.DTRefNum(a3), d0 ; is this the right database? beq.s @foundIt ; yes? Then stop looking move.l DTDBQElt.qLink(a3), a3 ; move on to the next one @nextPtr: move.l a3, d1 ; set condition codes to check for nil bne.s @WalkQueue ; something there? keep looking. ; If we get here, then we saw a nil pointer, which means none of the open databases ; matched the user's DTRefNum. Return a rfNumErr move.w #rfNumErr, d0 ; set result code bra.s @leave @foundIt: move.w #0, d0 ; no errors @leave: move.l (a6)+, d1 ; restore d1 tst.w d0 ; set the result code rts _DTDebugTail 'DTSetUp',0 endproc ;________________________________________________________________________________ ; ; Routine: CheckCSpec ; ; Function: Get info on a CNodeID in the catalog. Translate the user's ; DTRefNum into the right volume for the check. ; ; Inputs: a3 = DTDBQElt block for current database ; a4 = User's DTParam block ; ; Outputs: d0 = result code from _GetCatInfo on CNode ; d1 = CNodeID of the CNode we just checked ; ;________________________________________________________________________________ CheckCSpec: proc CCSLocals record 0, increment bigPB ds.b DTBigPBSize ; big enough to make any HFS or Btree call LSize equ *-bigPB endr @CCSRegs reg a0/a1 with CCSLocals move.l (sp)+, -(a6) ; save desktop return addr around i/o movem.l @CCSRegs, -(a6) ; save regs suba.w #LSize, a6 ; allocate locals on a6 lea.l bigPB(a6), a0 ; a0 = ptr(param block) move.l ioFileName(a4), ioFileName(a0) ; use user's name ptr move.w DTDBQElt.XVRefNum(a3), ioVRefNum(a0) ; use correct volume ref clr.w ioFDirIndex(a0) ; no indexing move.l ioDirID(a4), ioDirID(a0) ; use user's DirID go_GetCatInfo ; Look up this CNode's CNID move.l ioDirID(a0), d1 ; ioDirID now contains CNodeID, so return it add.w #LSize, a6 ; deallocate locals movem.l (a6)+, @CCSRegs move.l (a6)+, -(sp) ; restore desktop return address tst.w d0 rts endwith _DTDebugTail 'CheckCSpec',0 endproc ;________________________________________________________________________________ ; <21> ; Routine: MyFindFolder/MyMakeFolder ; ; A little wrapper for FindFolder that is register-based. ; Currently, we implement two extra specialties the are handy for us ; 'root' returns the root directory ; 'serv' returns the server directory (we won't create this, though) ; ; Inputs: ; d1.w vRefNum or wdRefNum we're interested in ; d2.l folder specialty ; ; Outputs: ; d0.w error code (ccr set) ; d1.w vRefNum ; d2.l directory ID ;________________________________________________________________________________ MyMakeFolder: proc entry myFindFolder moveq.l #kCreateFolder,d0 bra.s MakeFindCommon MyFindFolder: moveq.l #kDontCreateFolder,d0 MakeFindLocals record 0, increment vRef ds.w 1 ; stores the vRefNum of the volume under consideration dirID ds.l 1 ; stores the directory ID of the directory we're looking in bigPB ds.b ioHVQElSize ; enough for an HGetVolInfo LSize equ * endr MakeFindRegs reg a0/a1 FolderMgrSupportsRootAndServ equ 0 with MakeFindLocals MakeFindCommon: move.l (sp)+,-(a6) ; we do I/O <26> movem.l MakeFindRegs, -(a6) ; so we save on the A6 stack <26> sub.w #LSize,a6 if not(FolderMgrSupportsRootAndServ) then cmp.l #'root',d2 ; looking for the root directory? beq.s @mine cmp.l #'serv',d2 ; looking for the server folder? bne.s StandardFind @mine: cmp.w #kOnSystemDisk, d1 ; folder manager's special system disk indicator? bne.s @useTheVolume move.w BootDrive, d1 ; try the boot drive (a.k.a where the active system is) @useTheVolume: lea.l bigPB(a6),a0 ; a0 = ptr(param block) clr.l ioFileName(a0) ; we don't want the volume name move.w d1,ioVRefNum(a0) ; VRef of volume to look at clr.w ioVolIndex(a0) ; no indexing; use VRef only go_HGetVInfo bne.s @error ; punt on errors move.w ioVRefNum(a0),d1 ; HGetVInfo extracts vRefs from wdRefs cmp.l #'root',d2 ; looking for the root directory? beq.s @root move.l ioVFndrInfo+28(a0),d2 ; d2 = DirID of server folder bne.s @test ; got one, success moveq.l #fnfErr,d0 ; just like the folder manager @error: @test: bra.s Done @root: moveq.l #fsRtDirID,d2 bra.s Done ; get out of here, and test the result code <28> StandardFind: endif subq.l #2,sp ; allocate a word for error code move.w d1,-(sp) ; look for the folder on this volume move.l d2,-(sp) ; look for this folder move.w d0,-(sp) ; tell whether to create pea.l vRef(a6) ; stuff the volume in here pea.l dirID(a6) ; and the directory here ; Note: This is call will do synchronous I/O, which is strictly illegal in ; the Desktop Manager, since Desktop Manager calls can trigger requests on ; the front the File System queue (which could themselves be Desktop Manager ; calls) and a executing a syncwait for the triggered call could hang the ; system. The only solace we can take is that this call is executed only ; when a volume is mounted, so it won't hang the system very often. _FindFolder move.w vRef(a6),d1 ; grab the volume move.l dirID(a6),d2 ; grab the directory move.w (sp)+,d0 ; grab the error Done: add.w #LSize,a6 movem.l (a6)+,MakeFindRegs ; get the stuff back <26> move.l (a6)+,-(sp) ; we did I/O <26> tst.w d0 ; make sure we set the ccr <28> rts endwith endproc ;________________________________________________________________________________ ; <21> ; Routine: FindDTFiles ; ; Find the desktop database files, if they exist. If they don't exist, return the ; appropriate location and name for them. ; ; The desktop database files are always a pair of files with names that differ ; only in the last character. For normal desktop files, the names are 'Desktop DB' ; and 'Desktop DF'. DB is a btree file, DF is a data file with icons in it. For ; shadow desktops (databases maintained on a different volume to support a volume that ; isn't writeable) the files are named ' DDB' and ' DDF' ; ; The idea: On unlocked volumes, we'll look for a database file in the root (the normal spot), ; then server folder (in case the volume is a server that had a file created ; there by the old desktop manager), and the system folder (in case the volume is a ; regular hard drive and the user was running the old Desktop Manager INIT). ; If we don't find one, we'll return the root as the place to create one. ; ; On locked volumes, we'll do the first part of above, then take a look ; in the currently active preferences folder and system folder for a shadow database. ; If we strike out both above and in the active system folder, we'll return the active ; preferences folder as the place to create the shadow database. ; ; Inputs: a1.l - pointer to 32 character buffer ; d0.w - vRefNum of volume to open DT on ; ; Outputs: a0.l - preserved ; a1.l - ptr(Name for btree file). Datafile name is always formed from btree file name ; by changing the last character from a 'B' to an 'F' ; d0.w - result code (error invalidates d1/d2/d3) ; d1.w - VRefNum of volume containing actual database files ; d2.l - ParID of directory containing actual database files ; d3.b - Set true if the files exist, false if they don't ; If the files don't exist, they should be created in d1/d2 w/name (a1) ;________________________________________________________________________________ FindDTFiles: proc FindDTLocals record 0, increment bigPB ds.b DTBigPBSize ; big enough for all HFS calls LSize equ * endr FindDTRegs reg d4/a0/a2/a3 with FindDTLocals move.l (sp)+,-(a6) ; we do I/O <26> movem.l FindDTRegs,-(a6) ; save regs <26> sub.w #LSize,a6 ; allocate locals move.w d0,d3 ; save caller's requested volume ; Use the standard dt btree file name. lea.l StandardName, a0 ; a0 = ptr(the standard btree file name) moveq.l #0, d0 ; clear high bytes move.b (a0), d0 ; get length of string addq.b #1, d0 ; add one for the length byte _BlockMove ; copy standard name into caller's (a1) buffer move.l #'root',d2 ; try the root first bsr CheckDTExists beq Found move.l #'serv',d2 ; try the server folder next bsr CheckDTExists beq Found move.l #kSystemFolderType,d2 ; try the server folder next bsr CheckDTExists beq Found lea.l bigPB(a6),a0 ; a0 = ptr(param block) clr.l ioFileName(a0) ; we don't want the volume name move.w d3,ioVRefNum(a0) ; vRef of volume to look at clr.w ioVolIndex(a0) ; no indexing; use VRef only go_HGetVInfo bne Error ; punt on errors CSS move.w bigPB+ioVAtrb(a6), d0 ; get the volume attributes btst.l #15, d0 ; software locked? bne.s VolLocked btst.l #7, d0 ; hardware locked? bne.s VolLocked ; if not, then we need to make new files move.l #'root',d2 ; make it in the root bra.s Create VolLocked: ; Replace the standard name with the name of the volume we're shadowing lea.l bigPB(a6),a0 ; a0 = ptr(param block) move.l a1,ioFileName(a0) ; tell the param block where to store the vol. name move.w d3,ioVRefNum(a0) ; vRef of volume to look at clr.w ioVolIndex(a0) ; no indexing; use VRef only go_HGetVInfo bne.s Error ; punt on errors ; Transform volume name into shadow btree file name by adding StandardShadow to it. moveq.l #0,d0 ; clear high bytes moveq.l #0,d2 movea.l a1, a2 ; save base address of name string lea.l StandardShadow,a0 ; a0 = ptr(shadow pstring) move.b (a0)+,d0 ; d0 = number of characters to copy move.b (a1),d2 ; d2 = length of volume name add.b d0,(a1)+ ; update length to include shadow characters add.w d2,a1 ; point to end of volume name _BlockMove movea.l a2, a1 ; restore a1 to point as base of string move.w #kOnSystemDisk,d3 ; now we're looking on the system disk for shadow databases move.l #kPreferencesFolderType,d2 ; try the preferences folder first bsr.s CheckDTExists beq.s Found move.l #'serv',d2 ; try the server folder next bsr CheckDTExists beq.s Found move.l #kSystemFolderType,d2 ; try the system folder last bsr.s CheckDTExists beq.s Found move.l #kPreferencesFolderType,d2 ; make it in the preferences folder Create: move.w d3,d1 ; get volume to create on bsr MyMakeFolder bne.s Error moveq.l #0,d3 ; indicate files don't exist bra.s Done Found: moveq.l #1,d3 ; indicate that files exist Error: Done: add.w #LSize,a6 movem.l (a6)+,FindDTRegs ; restore <26> move.l (a6)+,-(sp) ; we did I/O <26> tst.w d0 ; be nice and set up flags <28> rts ; This routine assumes the a6 stack from FindDTFiles ; ; input: d3.w the volume to check ; d2.l the special folder to check ; a1.l ptr(name to look for) ; ; output: d1.w volume found on ; d2.l directory found in ; d0.w error code CheckDTExists: move.l (sp)+,-(a6) ; we do I/O <26> move.w d3,d1 bsr myFindFolder ; convert d2 from specialty to dirID bne.s @error lea.l bigPB+4(a6), a0 ; a0 = ptr(param block) (add 4 for ret. addr. on a6) <28> move.l a1,ioFileName(a0) ; we want info on this file move.w d1,ioVRefNum(a0) ; look on our current volume clr.w ioFDirIndex(a0) ; no indexing; look by dirID and CName only move.l d2,ioDirID(a0) ; in the proper directory go_HGetFileInfo @error: move.l (a6)+,-(sp) ; we did I/O <26> tst.w d0 ; make sure we set the ccr's <28> rts _DTDebugTail 'FindDTFiles',18 ; the names = 18 bytes endwith string pascal StandardName: dc.b 'Desktop DB' ; of the standard name for the desktop database btree file align StandardShadow: dc.b ' DDB' ; suffix for shadow i.e. 'Hard Disk' becomes 'Hard Disk DDB' align string asis endproc ;________________________________________________________________________________ ; <1.7><1.9><25> ; Routine: DesktopNotifyProc ; ; Inputs notifyFrame stack frame ; ; Outputs: result from CloseDT call (normally noErr) ; We don't do anything if FindDTVol shows that desktop database is closed ; ; Function: Flush the desktop database on Offline calls ; Flush the desktop database on Unmount calls ; ;________________________________________________________________________________ DesktopNotifyProc: proc @DesktopNotifyProcRegs reg a1/a3/d0 link a6, #0 ; set up a stack frame movem.l @DesktopNotifyProcRegs, -(sp) with notifyFrame move.l vnbPtr(a6), a1 ; a1 = ptr(volume notice block) move.w VNBNotice(a1), d0 ; d0 = notice value cmp.w #VNAboutToGoOffline, d0 ; is this an offline notification? beq.s @DoOfflineAction ; go do our stuff cmp.w #VNAboutToUnmount, d0 ; is this an unmount notification? bne.s @SuccessExit ; no? then leave ; This is an offline or unmount notification. Try to flush the desktop database for the ; indicated volume. @DoOfflineAction: move.w VNBVolume(a1), d0 ; get the volume for which we're being notified bsr FindDTVol ; try to find a DTDBQElt for this volume bne.s @SuccessExit ; no database? Smile and leave sub.w #ioDTQElSize, sp ; allocate a DT param block movea.l sp, a0 ; a0 = ptr(our DT param block) move.w DTDBQElt.DTRefNum(a3), ioRefNum(a0) ; stash the DTRefNum for this volume _DTFlush move.w d0, theErr(a6) ; return the error from CloseDT add.w #ioDTQElSize, sp ; deallocate the DT param block bra.s @Exit ; and leave @SuccessExit: move.w #noErr, theErr(a6) ; indicate no problems @Exit: movem.l (sp)+, @DesktopNotifyProcRegs unlk a6 ; undo the stack frame move.l (sp)+, a0 ; get the return address add.w #4, sp ; blow away the input parameter jmp (a0) ; return to caller _DTDebugRts 'DesktopNotifyProc', 0 endproc ;________________________________________________________________________________ ; ; Routine: RealOpenDT ; ; Inputs: a0 - pointer to caller's DT param block ; uses only ioVRefNum ; ; Outputs: d0 - result code ; d1.b - file exists flag ; true = files existed before this open call; they were just opened ; false = files did not exist before call; they were just created ; ; Function: Open an access path to the Desktop database. If the database is already ; open for the specified volume, return its refNum. ; ; This call is synchronous only. Yes, those move.l (sp)+, -(a6) lines ; could be removed someday. ;________________________________________________________________________________ RealOpenDTLocals record 0, increment bigPB ds.b DTBigPBSize ; big enough to make any HFS or Btree call userVRef ds.w 1 ; User's requested vRefNum dbName ds.b 32 ; Space for name of database files volParmBuf equ dbName ; 32 bytes is fine for a _GetVolParms buffer <20> existsFlag ds.b 1 ; true if the files already existed align LSize equ *-bigPB endr entry SetFileAttribs entry DTKeyCmp RealOpenDT: proc export @OpenDTRegs reg a0-a4/d2 with RealOpenDTLocals move.l (sp)+, -(a6) ; we do i/o movem.l @OpenDTRegs, -(a6) suba.w #LSize, a6 movea.l a0, a4 ; move user's DTParam to a safe register ; ; Grab the volume that this call is aimed at (determined when checking for external file systems) ; move.l FSVarsPtr, a3 ; a3 = ptr(file system global area) move.l FSVars.DTDBMgr(a3), a3 ; a3 = ptr(DTDB manager's global area) move.w DTGlobals.targetVRef(a3),userVRef(a6) ; pick up caller's requested volume (true vRef) ; ; First, check to make sure that this volume's DT isn't already open ; move.w userVRef(a6), d0 ; is this volume's database already open? bsr FindDTVol ; go look for DT files (DTDBQElt ptr in a3 if success) bne.s @NotAlreadyOpen ; if not, we'll have to open it ; ; Everything's cool - this really is an already opened database. ; move.w DTDBQElt.DTRefNum(a3), ioDTRefNum(a4) ; put the DTRefNum into user's PB st.b existsFlag(a6) ; say we got it open, and it was already there <20> bra @SuccessExit ; get out <20> ; ; The database is not currently open on the given volume. Go open it. ; @NotAlreadyOpen: ; <1.9> ; Make sure that this volume supports a desktop database ; lea.l bigPB(a6), a0 ; a0 = ptr(param block) move.w userVRef(a6), ioVRefNum(a0) ; fill in the volume ref clr.l ioNamePtr(a0) ; no volume name lea.l volParmBuf(a6), a1 ; big enough for dumb _GetVolParms implementors <20> move.l a1, ioBuffer(a0) ; and fill it in move.l #vMAttrib+4, ioReqCount(a0) ; and say much we want <20> go_GetVolParms bne @NoSupportExit ; punt on errors <20> move.l vMAttrib(a1), d0 ; get the volume bits btst.l #bHasDesktopMgr, d0 ; desktop database support? beq @NoSupportExit ; no? Then don't open ; ; See if the files exist already ; lea.l dbName(a6), a1 ; a1 = ptr(space for btree file name) move.w userVRef(a6), d0 ; d0 = volume to look for files for bsr FindDTFiles ; go look for DT files (d1 = vol, d2 = dir, d3 = flg) bne @OpenDTExit ; punt on serious errors move.b d3, existsFlag(a6) ; do the files need to be created? (i.e. d3 = false) bne @OpenFiles ; If they don't, go open them now ; ; The files didn't exist, but d1 has the vRef to create them on, d2 has the directory to ; create them in and a1 points to the name to create for the Btree file. ; lea.l bigPB(a6), a0 ; a0 = ptr(param block) moveq.l #0, d0 ; clear high bytes move.b (a1), d0 ; get length of btree file name move.b #'F', (a1, d0.w) ; change name to datafile name move.l a1, ioFileName(a0) ; use datafile name move.w d1, ioVRefNum(a0) ; use volume from FindDTFiles move.l d2, ioDirID(a0) ; use directory from FindDTFiles go_HCreate ; create the data file bne @OpenDTExit ; punt on errors move.l #DTDataFileType, d3 ; set the data file type bsr SetFileAttribs ; and the appropriate attrib bits bne @DeleteDataExit moveq.l #0, d0 ; clear high bytes move.b (a1), d0 ; get length of data file name move.b #'B', (a1, d0.w) ; change name to btree name go_HCreate ; create the file that will become the btree file bne @DeleteDataExit ; errors? Delete data file and leave move.w #DTMaxKeySize, ioBTMaxKLen(a0) ; set maximum key size clr.l ioBTClumpSize(a0) ; use volume clump size pea.l DTKeyDescriptor ; push pointer to our key descriptor move.l (sp)+, ioBTKDPtr(a0) ; and tell the btree about it go_BTInit ; initialize btree file bne @DeleteBothExit move.l #DTBTFileType, d3 ; set the btree file type bsr SetFileAttribs ; and the appropriate attrib bits bne @DeleteBothExit ; ; Try to open the files: allocate space in the system heap for a DTDBQElt and do the opens ; @OpenFiles: move.l #DTDBQElt.Size,d0 ; Size of DT variable block _NewPtr sys, clear ; Try to allocate the space in the system heap bne @OpenDTExit ; give up if there's no memory movea.l a0, a3 ; a3 = ptr(DTDBQElt) @OpenBTFile: lea.l bigPB(a6), a0 ; a0 = ptr(param block) move.l a1, ioFileName(a0) ; use btree name from FindDTFiles move.w d1, ioVRefNum(a0) ; use vRef from FindDTFiles move.b #fsRdWrPerm, ioPermssn(a0) ; grab write permission move.l d2, ioDirID(a0) ; use DirID from FindDTFiles pea.l DTKeyCmp ; use our own key comparison procedure move.l (sp)+,ioBTKCProc(a0) go_BTOpen bne @DeallocateExit ; if not, that's trouble <20> move.w ioRefNum(a0), DTDBQElt.DBRefNum(a3) ; save away the btree file refnum moveq.l #0, d0 ; clear high bytes move.b (a1), d0 ; get length of database name move.b #'F', (a1, d0.w) ; change last character to an F move.b #fsRdWrPerm, ioPermssn(a0) ; grab write permission clr.l ioOwnBuf(a0) ; use volume buffer go_HOpen bne @CloseBTExit ; punt on errors move.w ioRefNum(a0), DTDBQElt.DFRefNum(a3) ; save away the data file refnum ; ; So far, so good. Both files are open, and we've got a QElt ready. ; Fill in the rest of the info, enqueue it, get unmount notification, and we're done ; move.w DTDBQElt.DBRefNum(a3), DTDBQElt.DTRefNum(a3) ; DTRefNum = ioRefNum of the btree file move.w DTDBQElt.DBRefNum(a3), ioDTRefNum(a4) ; return DTRefNum to caller clr.b DTDBQElt.Flags(a3) ; clear out our flags clr.b DTDBQElt.Reserved(a3) ; clear out the reserved byte move.w userVRef(a6), DTDBQElt.XVRefNum(a3) ; save user's target volume move.w d1, DTDBQElt.VRefNum(a3) ; save the real volume where files are move.l d2, DTDBQElt.ParID(a3) ; and the real directory move.l #DTDataClump, DTDBQElt.DFClumpSize(a3) ; and an arbitrary clump clr.l DTDBQElt.lastHint(a3) ; no btree hint yet move.l FSVarsPtr, a1 ; a1 = ptr(file system global area) move.l FSVars.DTDBMgr(a1), a1 ; a1 = ptr(DTDB manager's global area) lea.l DTGlobals.qFlags(a1), a1 ; a1 = ptr(DTQueue header) movea.l a3, a0 ; move DTDBQElt ptr into a0 _Enqueue tst.w d0 ; enqueue is a toolbox trap beq.s @SkyStillUp jmp deepShitError ; if the sky falls, duck @SkyStillUp: ; ; <21> If the files just got created, try to allocate a contiguous clump just for performance. ; tst.b existsFlag(a6) ; are we brand new? bne.s @alreadyExisted ; no, don’t allocate a clump lea.l bigPB(a6), a0 ; a0 = ptr(param block) <22> move.l DTDBQElt.DFClumpSize(a3), ioReqCount(a0) ; d1 = the data file's clump size go_Allocate ; request the additional space @alreadyExisted: ; ; <1.7> Install MultiFinder unmount notification ; move.l FSVarsPtr, a1 ; a1 = ptr(file system global area) move.l FSVars.DTDBMgr(a1), a1 ; a1 = ptr(DTDB manager's global area) bset.b #notifyBit, DTGlobals.infoFlags(a1) ; do we already have notification installed? bne.s @skipNotify ; yes? -> lea.l -2(sp), sp ; leave space for OSErr output pea.l DesktopNotifyProc ; the routine called at notification time move.l #'KWAK', -(sp) ; we don't have a refCon bsr myBeginSystemMode ; be the system _RequestVolumeNotification ; sign up for notify bsr myEndSystemMode ; be the app move.w (sp)+, d0 ; did we succeed? beq.s @ItWorked jmp deepShitError ; if not, the sky is falling @ItWorked: @skipNotify: @SuccessExit: ; all successful exits are through here <20> moveq.l #noErr, d0 ; we succeeded tst.b existsFlag(a6) ; return a result beq.s @OpenDTExit moveq #1,d1 ; files existed, so we return a 1 bra.s @OpenDTExitResultInD1 ; We failed while initializing the btree file, so delete both files before cruising @DeleteBothExit: move.w d0, ioResult(a4) ; save the error that got us here go_HDelete ; delete the btree file move.w ioResult(a4), d0 ; replace err w/real err that got us here ; We failed while creating the btree file, so delete the datafile before cruising @DeleteDataExit: move.w d0, ioResult(a4) ; save the error that got us here moveq.l #0, d0 ; clear high bytes move.b (a1), d0 ; get length of btree file name move.b #'F', (a1, d0.w) ; change name to data file name move.l a1, ioFileName(a0) ; use data file name go_HDelete ; delete the data file move.w ioResult(a4), d0 ; replace err w/real err that got us here bra.s @OpenDTExit ; We failed on opening the datafile, so close the btree file before punting @CloseBTExit: move.w d0, ioResult(a4) ; save the error that got us here lea.l bigPB(a6), a0 ; a0 = ptr(param block) move.w DTDBQElt.DBRefNum(a3), ioRefNum(a0) go_BTClose move.w ioResult(a4), d0 ; and make sure that it gets back to the user ; We allocated a DTDBQElt before failing, so deallocate it before punting @DeallocateExit: movea.l a3, a0 ; get DTDBQElt ptr move.w d0, -(sp) ; save failure-provoking result code _DisposPtr ; and free up DTDBQElt move.w (sp)+, d0 ; restore failure-provoking result code bra.s @OpenDTExit ; and leave @NoSupportExit: move.w #wrgVolTypErr, d0 ; indicate the boo-boo @OpenDTExit: moveq.l #0, d1 ; clear high bytes @OpenDTExitResultInD1: ; <20> adda.w #LSize, a6 ; deallocate locals movem.l (a6)+, @OpenDTRegs move.l (a6)+, -(sp) rts endwith _DTDebugTail 'OpenDT',0 endproc ; ========== ; Routine: SetFileAttribs (requires locals frame of RealOpenDT) ; ; Inputs: a0 - pointer to PB w/vRef, ioNamePtr, and ioDirID set ; a4 - pointer to OpenDT parameter block ; a6 - pointer to RealOpenDT locals (uses userVRef) (does compensation for saved addr) ; d2 - dirID of file ; d3 - file type (DTFL or BTFL) ; ; Outputs: registers preserved ; d0 - noErr or result code from call which failed ; ; Set the finder info (type in d3, creator DMGR) ; Set the attribute bits: ; regular desktops: invisible ; shadow desktops: visible ; ========== SetFileAttribs: proc with RealOpenDTLocals move.l (sp)+,-(a6) ; we do I/O <30> clr.w ioFDirIndex(a0) ; no indexing; look by dirID and CName only go_HGetFileInfo ; fill the param block with cat info <21> bne @errorExit move.l d3, ioFlUsrWds+fdType(a0) ; set the file type move.l #DTFileCreator, ioFlUsrWds+fdCreator(a0) ; set the file creator cmp.w userVRef+4(a6), d1 ; compare target volume to actual volume <30> bne.s @shadow ; if they're different, we're shadowing move.w #fInvisible, d0 ; bit pattern for invisible flag bra.s @setAttributes @shadow: moveq.l #0,d0 ; no special attributes @setAttributes or.w d0, ioFlUsrWds+fdFlags(a0) ; logical or into current attributes move.l d2, ioDirID(a0) ; set correct directory (GetCatInfo blasts it) clr.w ioFDirIndex(a0) ; no indexing; look by dirID and CName only go_HSetFileInfo ; set our new info <21> @errorExit: move.l (a6)+,-(sp) ; we did I/O <30> tst.w d0 ; restore those cute cc's <30> rts endproc ;________________________________________________________________________________ ; ; Routine: DTOpenInform ; ; Inputs: a0 - pointer to caller's DT param block ; uses only ioVRefNum ; ; Outputs: d0 - result code ; ; Function: Same as DTGetPath, except returns a flag in ioTagInfo indicating ; whether or not the files already existed. ; True = already existed ; False = were just created ;________________________________________________________________________________ DTOpenInform: proc export jsrROM FSQUEUESYNC ; just to check for external file systems bsr DTVolExtFSCheck ; find the right volume and hope it's ours bsr DeskMgrQueue bsr RealOpenDT ; d1 true/false for dtExisted and.l #1, d1 ; return low bit only <20> move.l d1, ioTagInfo(a0) bra DTDone _DTDebugRTS 'DTOpenInform', 0 endproc ;________________________________________________________________________________ ; ; Routine: DTGetPath ; ; Inputs: a0 - pointer to caller's DT param block ; uses only ioVRefNum ; ; Outputs: d0 - result code ; ; Function: Open an access path to the Desktop database. If the database is already ; open for the specified volume, return its refNum. ;________________________________________________________________________________ DTGetPath: proc export jsrROM FSQUEUESYNC ; just to check for external file systems bsr DTVolExtFSCheck ; find the right volume and hope it's ours bsr DeskMgrQueue bsr RealOpenDT bra DTDone _DTDebugRTS 'DTGetPath', 0 endproc ;________________________________________________________________________________ ; ; Routine: DequeueDTDBQElt ; ; Inputs: a0 - pointer to DTDBQElt to remove ; Outputs none ; ; Function: Dequeue and deallocate a DTDBQElt. No check of a0 is ; done. The block it points to must be both allocated ; and in the queue. ; Called by: CloseDT and VerifyOpenFiles ;________________________________________________________________________________ DequeueDTDBQElt: proc move.l a1, -(sp) move.l FSVarsPtr, a1 ; a1 = ptr(file system global area) move.l FSVars.DTDBMgr(a1), a1 ; a1 = ptr(DTDB manager's global area) lea.l DTGlobals.qFlags(a1), a1 ; a1 = ptr(DTQueue header) _Dequeue tst.w d0 ; 'cause _Dequeue doesn't test beq.s @Ok jmp deepShitError ; we're really up the creek if this fails @Ok: _DisposPtr ; free it up the element movea.l (sp)+, a1 rts _DTDebugTail 'DequeueDTDBQElt', 0 endproc ;________________________________________________________________________________ ; ; Routine: DTCloseDown ; ; Inputs: a0 - pointer to caller's DT param block ; uses only ioDTRefNum ; ; Outputs: d0 - result code ; ; Function: Close the desktop database for a volume ;________________________________________________________________________________ DTCloseDown: proc export DTCloseDownLocals record 0, increment bigPB ds.b DTBigPBSize ; big enough to make any HFS or Btree call LSize equ *-bigPB endr @DTCloseDownRegs reg a0-a4/d1-d2 jsrROM FSQUEUESYNC ; just to check for external file systems bsr DTRfnExtFSCheck ; find the right volume and hope it's ours bsr DeskMgrQueue with DTCloseDownLocals movem.l @DTCloseDownRegs, -(a6) suba.w #LSize, a6 movea.l a0, a4 ; move user's DTParam to a safe register bsr DTSetup ; Do common setup. a3 = ptr(DTDBQElt) bne.s @DTCloseDownExit lea.l bigPB(a6), a0 ; a0 = ptr(param block) move.w DTDBQElt.DBRefNum(a3), ioRefNum(a0) go_BTClose move.w DTDBQElt.DFRefNum(a3), ioRefNum(a0) go_Close ; same goes for here movea.l a3, a0 ; move DTDBQElt to a0 bsr DequeueDTDBQElt ; dequeue and dispose of it @DTCloseDownExit: adda.w #LSize, a6 ; deallocate locals movem.l (a6)+, @DTCloseDownRegs bra DTDone endwith _DTDebugRts 'DTCloseDown',0 endproc ;________________________________________________________________________________ ; ; Routine: DTAddIcon ; ; Input: a0 - user's DT param block ; Output: d0 - result code ; ; Function: Add an icon bitmap to the Desktop database under its FileType/Creator ;________________________________________________________________________________ ; DTAddIcon: proc export StandardLocals AddIconLocals, IconDRec ; normal locals w/icon sized data rec @AddIconRegs reg a0-a4/d1-d2 ; set of regs to save around AddIcon jsrROM FSQUEUE ; just to check for external file systems bsr DTRfnExtFSCheck ; find the right volume and hope it's ours bsr DeskMgrQueue ; Wait our turn. with AddIconLocals ; Key and Data refer to locals movem.l @AddIconRegs, -(a6) suba.w #LSize, a6 ; allocate locals on the a6 stack movea.l a0, a4 ; move user's DTParam to a safe register bsr DTSetup ; Do common setup. a3 = ptr(DTDBQElt) bne @AddIconExit ; and punt on errors move.l ioReqCount(a0), d0 ; d0 = user's icon data size cmp.l #DTMaxIconSize, d0 ; icon too big? ble.s @SizeOK ; then keep going moveq #paramErr, d0 ; otherwise, complain bra @AddIconExit ; and cruise ; ; First, search the btree to see if there's an icon with the same key already in the database ; @SizeOK: bsr SetUpBTPB ; standard BT param in a0 w/btree refNum move.l #IconDRec.IRLen, ioReqCount(a0) ; indicate size of icon data record move.b #IconKey.IKLen-1, (a2)+ ; Set length of key (minus length byte) move.b #IconKeyType,(a2)+ ; ... and key type move.l ioFlCreator(a4),(a2)+ ; Set up CreatorType move.l ioFlType(a4),(a2)+ ; ... and FileType move.b ioIconType(a4),(a2)+ ; Pass in the requested icon Type clr.b (a2)+ ; clear the filler byte <1.7> go_BTSearch bsr DoneBTPB ; finished with BTPB <21> beq.s @ChkSize ; If no errors, check icon size cmp.w #btRecNotFnd, d0 ; Entry not found? beq.s @AddNew ; No problem - go create a brand new one bra @AddIconExit ; Other errors are more serious ; ; An entry for an icon of the same FlCreator/FlType/IconType already exists, ; make sure its the same size as the requested new one. ; @ChkSize: lea.l Data(a6), a2 ; a2 = ptr(B*-Tree Icon data record) move.w IconDRec.IconSize(a2), d0 ; Pick up current icon size cmp.w ioReqCount+2(a4), d0 ; Same size as replacement? beq.s @CanWrite ; Yes - go overwrite it move.w #afpIconTypeError,d0 bra @AddIconExit ; otherwise, fail miserably @CanWrite: move.l IconDRec.IconPos(a2), d0 ; Pick up old position bra.s @AIWriteIcon ; And go overwrite the old icon bits ; ; We'll have to allocate space in the DF file for a new entry. Just add to the end. ; @AddNew: bsr SetUpDFPB ; Get an ioParam on a0 w/data file refnum go_GetEOF ; Find LEOF of the datafile bne.s @AddIconExit ; Problems? See your registered pharmacist. move.l ioLEOF(a0), d0 ; Pick up position to write icon at ; ; Arrive here from CanWrite or AddNew with d0 = position to write icon bits in Desktop DF: ; Write the icon into the data file, and if that succeeds, add the icon record to the Btree. ; @AIWriteIcon: lea.l Data(a6), a2 ; a2 = ptr(B*-Tree Icon data record) move.l ioTagInfo(a4),IconDRec.TagInfo(a2) ; Copy tag information into data record move.l d0,IconDRec.IconPos(a2) ; save file position in icon btree rec move.w ioReqCount+2(a4),IconDRec.IconSize(a2) ; Copy size of icon ; a quick check to see if we need to extend the data file move.w DTDBQElt.DFRefNum(a3), d1 ; use the datafile's refNum move.l FCBSPtr,a1 ; a1 = ptr(FCB array) lea.l 0(a1,d1.w),a1 ; a1 = ptr(datafile's FCB) move.l d0,d2 ; move icon's file position to d2 for some calculating add.l ioReqCount(a4),d2 ; d2 = file position + bitmap size cmp.l fcbPLen(a1),d2 ; Check against the current PEOF bls.s @AIGoWrite ; If icon end < PEOF, start writing ; To minimize fragmentation, we extend the icon datafile by a bunch each time bsr SetUpDFPB ; Get an ioParam on a0 w/data file refnum move.l DTDBQElt.DFClumpSize(a3), ioReqCount(a0) ; d1 = the data file's clump size go_Allocate ; Request the additional space ; The file's big enough, so write the icon data to the datafile @AIGoWrite: bsr SetUpDFPB ; Get an ioParam on a0 w/data file refnum move.l ioBuffer(a4), ioBuffer(a0) ; data comes from user's pointer clr.l ioReqCount(a0) ; size is just a word move.w ioReqCount+2(a4), ioReqCount+2(a0) ; get size from user pb move.w #fsFromStart, ioPosMode(a0) ; position is relative to byte 0 move.l IconDRec.IconPos(a2), ioPosOffset(a0) ; use the icon pos we've computed go_Write ; Fire off the write bne.s @AddIconExit ; punt on errors ; set the icon record in the Btree file bsr SetUpBTPB ; standard BT param in a0 w/btree refNum move.l #IconDRec.IRLen, ioReqCount(a0) ; tell the btree about the length go_BTSetRec ; the key is still cool from the search bsr DoneBTPB ; finish up with the BTPB <21> @AddIconExit: adda.w #LSize, a6 ; get rid of our locals movem.l (a6)+, @AddIconRegs ; and restore our registers bra DTDone ; We're all set. endwith _DTDebugRts 'DTAddIcon',0 endproc ;________________________________________________________________________________ ; ; Routine: DTGetIcon ; ; Input: a0 - user's DT param block ; Output: d0 - result code ; ; Function: Retrieve an icon from the Desktop database by Creator/Type ;________________________________________________________________________________ ; DTGetIcon: proc export StandardLocals GetIconLocals, IconDRec @GetIconRegs reg a0-a4/d1-d2 ; set of regs to save around AddIcon jsrROM FSQUEUE ; just to check for external file systems bsr DTRfnExtFSCheck ; find the right volume and hope it's ours bsr DeskMgrQueue ; Wait our turn. with GetIconLocals ; Key and Data refer to locals movem.l @GetIconRegs, -(a6) suba.l #LSize, a6 ; allocate locals movea.l a0, a4 ; move user's DTParam to a safe register bsr DTSetup ; Do common setup. a3 = ptr(DTDBQElt) bne.s @GetIconExit ; punt on errors ; go look for the requested icon's entry in the btree bsr SetUpBTPB ; a0 = ptr(BTioParam) move.l #IconDRec.IRLen, ioReqCount(a0) ; indicate size of icon data record move.b #IconKey.IKLen-1, (a2)+ ; Stuff key length (minus length byte) move.b #IconKeyType, (a2)+ ; and the type move.l ioFlCreator(a4), (a2)+ ; creator from user's DTPB move.l ioFlType(a4), (a2)+ ; type from user's DTPB move.b ioIconType(a4), (a2)+ ; icon type from user's DTPB clr.b (a2)+ ; clear the filler byte <1.7> go_BTSearch bsr DoneBTPB ; finish up with the BTPB <21> bne.s @GetIconExit ; punt on errors move.l IconDRec.TagInfo+Data(a6), ioTagInfo(a4) ; return tag information ; ; Read the icon from the data file ; bsr SetUpDFPB ; Get an ioParam on a0 w/data file refnum move.l ioBuffer(a4), ioBuffer(a0) ; data goes to the user's buffer moveq.l #0, d0 ; clear high word move.w IconDRec.IconSize+Data(a6), d0 ; d0 = size of icon data cmp.w ioReqCount+2(a4), d0 ; is the user's size big enough? bls.s @sizeOk ; yes, read the whole icon move.w ioReqCount+2(a4), d0 ; otherwise clip to provided space @sizeOk: move.l d0, ioReqCount(a0) ; the correct length move.w #fsFromStart, ioPosMode(a0) ; position from start of file move.l IconDRec.IconPos+Data(a6), ioPosOffset(a0) ; get position from icon's btree rec go_Read ; go get the icon data bne.s @GetIconExit ; punt on errors move.l ioActCount(a0),ioActCount(a4) ; Return actual byte count @GetIconExit adda.w #LSize, a6 ; deallocate locals movem.l (a6)+, @GetIconRegs bra DTDone ; We're all set. endwith _DTDebugRts 'DTGetIcon',0 endproc ;________________________________________________________________________________ ; ; Routine: DTGetIconInfo ; ; Input: a0 - user's DT param block ; Output: d0 - result code ; ; Function: Retrieve an icon from the Desktop database by Creator/Index ;________________________________________________________________________________ ; DTGetIconInfo: proc export StandardLocals GetIconInfoLocals, IconDRec @GetIconInfoRegs reg a0-a4/d1-d2 ; set of regs to save around this call jsrROM FSQUEUE ; just to check for external file systems bsr DTRfnExtFSCheck ; find the right volume and hope it's ours bsr DeskMgrQueue ; Wait our turn with GetIconInfoLocals ; Key and Data refer to locals movem.l @GetIconInfoRegs, -(a6) suba.w #LSize, a6 movea.l a0, a4 ; move user's DTParam to a safe register bsr DTSetup ; Do common setup. a3 = ptr(DTDBQElt) bne @GetIconInfoExit2 ; Punt on any error ; Do a search. We know we won't find anything, but the hint will put us just ; to the "left" of all of the icons with this icon creator bsr SetUpBTPB ; a0 = ptr(BTioParam), a2 = ptr(key) move.l #IconDRec.IRLen, ioReqCount(a0) ; we know how big the record is move.b #05, (a2)+ ; special short key (•• magic constant) move.b #IconKeyType, (a2)+ ; standard icon type move.l ioFlCreator(a4), (a2)+ ; user's requested creator go_BTSearch cmp.w #btRecNotFnd, d0 ; was the error a search miss? bne.s @GetIconInfoExit ; otherwise, something's wrong ; With the ioBTHint set up, go get the indexed record move.w ioIndex(a4), d0 ; get the caller's index bne.s @IndexOK ; did the user use a silly index, like zero? move.w #btRecNotFnd, d0 ; well, that one doesn't exist bra.s @GetIconInfoExit ; so leave @IndexOK: subq.w #1, d0 ; indicies start at 1, so adjust offset move.w d0, ioBTPosMode(a0) ; record offset from current position move.w #IconKey.IKLen, ioKReqCount(a0) ; we know how big the key is (including length byte here) go_BTGetRec bne.s @GetIconInfoExit ; Bad day at the office? then leave. move.b IconKey.Type+Key(a6), d0 ; get the key id cmp.b #IconKeyType, d0 ; is it still an icon key? beq.s @CheckCreator ; if so, go see if the creator is still the same move.w #afpItemNotFound, d0 ; otherwise, the index was too high bra.s @GetIconInfoExit ; so return an error @CheckCreator: move.l IconKey.CrType+Key(a6), d0 ; pick up the icon's creator type cmp.l ioFlCreator(a4), d0 ; check against user's requested type beq.s @FoundGoodOne ; same = good move.w #afpItemNotFound, d0 ; otherwise, index was too high bra.s @GetIconInfoExit ; so return an error @FoundGoodOne: move.l IconDRec.TagInfo+Data(a6), ioTagInfo(a4) ; return this icon's tag clr.l ioActCount(a4) ; clear whole long move.w IconDRec.IconSize+Data(a6), ioActCount+2(a4); and return word-sized icon size move.b IconKey.IconType+Key(a6), ioIconType(a4) ; return this icon's iconType move.l IconKey.FlType+Key(a6), ioFlType(a4) ; return this icon's fileType moveq.l #0, d0 ; All is well. @GetIconInfoExit: bsr DoneBTPB ; finish up with the BTPB <21> @GetIconInfoExit2: adda.w #LSize, a6 ; deallocate locals movem.l (a6)+, @GetIconInfoRegs bra DTDone ; We're all set. endwith _DTDebugRts 'DTGetIconInfo',0 endproc ;________________________________________________________________________________ ; The Application List ; ; Applications are stored in the app list by their creator and a synthetic 16-bit ; sequence number. The seqnums exist to provide unique keys and ordering to each ; application with a given creator. ; ; The two app list routines which need to manipulate the list (add and delete) ; use a common iterator function that calls them once for each application in ; the list. The iterator runs from higher seqnums to lower seqnums. (right to ; left). ; ; Apps are kept in the list so that the leftmost entry is always the youngest ; (as recorded by the creation date of the application file). ;________________________________________________________________________________ ;________________________________________________________________________________ ; Routine: SetUpAPPLIterator ; ; Input: a2 - standard locals (param block, key, data) ; a3 - dtdbqelt pointer ; d1 - creator type ; ; Output: a0 - BT param block with valid hint, key and data sizes ; d0 - result code from BTSearch (btRecNotFound error is suppressed) ; ; Function: Initialize a btree param block to point one to the right of ; the "first" (i.e. rightmost) APPL entry of the given creator. ; A btRecNotFound error is expected since we're using a short key, ; so return noErr if we see it. All other errors are passed through. ; ;________________________________________________________________________________ ; SetUpAPPLIterator: proc move.l (sp)+, -(a6) ; <18> with APPLLocals ; Key and Data are application-sized move.l a1, -(a6) lea.l bigPB(a2), a0 ; a0 = ptr(btree param block) bsr GetHintFromDTDBQElt ; fetch the hints move.w DTDBQElt.DBRefNum(a3), ioRefNum(a0) ; use the btree file's refnum lea.l Data(a2), a1 ; a1 = ptr(APPL record data block) move.l a1, ioBuffer(a0) lea.l Key(a2), a1 ; a1 = ptr(APPL key block) move.l a1, ioBTKeyPtr(a0) move.b #5, (a1)+ ; use short key so we'll be at the far left move.b #APPLKeyType, (a1)+ ; this is an APPL key move.l d1, (a1) ; with the requested creator add.l #1, (a1)+ ; tweak the key up to the next higher creator move.l #APPLRec.ARLen, ioReqCount(a0) ; set for later indexed BTGetRec calls move.w #APPLKey.AKLen, ioKReqCount(a0) ; set for later indexed BTGetRec calls go_BTSearch ; go miss the target move.l d1, APPLKey.SeqNum+Key(a2) ; restore correct creator type cmp.w #btRecNotFnd, d0 ; did we miss (as expected?) beq.s @NoErrExit ; yes? Leave with a smile bra.s @Exit ; otherwise pass the error through @NoErrExit: moveq.l #0, d0 @Exit: tst.w d0 move.l (a6)+, a1 move.l (a6)+, -(sp) ; <18> rts endwith _DTDebugTail 'SetUpAPPLIterator',0 endproc ;________________________________________________________________________________ ; Routine: APPLIterator ; ; Input: a0 - BT param block with valid hint, key, and data pointers ; a1 - callback procedure ; a2 - standard locals (param block, key, data) ; d1 - creator type ; ; Output: a0 - BT param block with valid hint, key, and data pointers ; a1 - callback procedure ; a2 - standard locals (param block, key, data) ; d0 - noErr if normal termination; terminating error code otherwise ; d1 - creator type ; ; Function: This iterator is used by the APPL calls (Add, Remove, and Get). ; It iterates through all APPL records with a creator type that ; matches the one in the user's param block. For each record, ; a callback procedure is called. Iteration continues until either ; no more APPL records with the right creator are available or ; the callback procedure returns a zero in d0. ; ; Registers a3-a5 and d2-d7 are passed directly to the callback ; procedure; it is responsible for restoring all registers except d0. ; ; Index through the records backwards (ffff through 0) to maintain ; compatibility with AppleShare's desktop manager. ;________________________________________________________________________________ ; APPLIterator: proc move.l (sp)+, -(a6) ; <18> with APPLLocals move.w #-1, ioBTPosMode(a0) ; get records in descending key order @Iterate: go_BTGetRec ; get the next record beq.s @Examine ; if noErr, check for reasonableness cmp.w #btBofErr, d0 ; are we out of records? bra.s @Exit ; else, a real error has ocurred @Examine: move.b APPLKey.Type+Key(a2), d0 ; get this record's key type cmp.b #APPLKeyType, d0 ; is this an APPL key? bne.s @NoErrExit ; if not, we're out of reasonable records move.l APPLKey.CrType+Key(a2), d0 ; get this record's creator cmp.l d0, d1 ; is this the right creator? bne.s @NoErrExit ; if not, we're out of reasonable records jsr (a1) ; give the caller a shot at it tst.w d0 ; are they all through? beq.s @NoErrExit bra.s @Iterate @NoErrExit: moveq.l #0, d0 @Exit: move.l (a6)+, -(sp) ; <18> rts endwith _DTDebugTail 'APPLIterator',0 endproc ;________________________________________________________________________________ ; Routine: APPLCompareNames ; ; Input: a2 - standard APPL locals ; ; Output: d0 - trashed ; ccr state from _CmpString of names ; ; Function: ; This routine compares the name from the current APPL rec (sitting in the ; APPL locals) to the name in the AppSpec (sitting in the APPL locals). ; ; You must have set up the AppSpec in APPLLocals before using this routine. ;________________________________________________________________________________ APPLCompareNames: proc with APPLLocals @stash reg a0/a1 movem.l @stash, -(sp) ; save away for a sec moveq.l #0, d0 ; clear high bytes lea.l AppSpec+FSSpec.name(a2), a0 ; a0 = ptr(leaf name of app) move.b (a0)+, d0 ; d0 = length, a0 = ptr(1st char) swap d0 ; length of 1st string into high word lea.l APPLRec.CName+Data(a2), a1 ; a1 = ptr(CName of current entry) move.b (a1)+, d0 ; d0 = length, a1 = ptr(1st char) _FSCmpString ; are the names the same? movem.l (sp)+, @stash rts endwith _DTDebugTail 'APPLCompareNames', 0 endproc ;________________________________________________________________________________ ; Routine: AddAPPLCallback ; ; Input: a0 - BT param block with valid hint, key, and data pointers ; a1 - callback procedure ; a2 - standard APPL locals ; a3 - DTDBQElt pointer ; a4 - user DT param block ; d1 - creator type ; ; Output: a0 - BT param block ; a1 - callback procedure ; a2 - standard locals (param block, key, data) ; a3 - DTDBQElt pointer ; a4 - user DT param block ; d0 - zero to signal end of iteration; nonzero otherwise ; d1 - creator type ; ; Function: ; This routine will get called once per APPL entry with a creator matching the ; one of the application being added to the list. ; It does these things: ; Look for a sequence number at which to add the new application. ; Keep track of the creation date and sequence number of the last record it saw ; Look for a duplicate entry, which means we don't need to insert anything. ; ; If no free sequence number is found when scanning the list, index(a2) will ; be one to the left of the last existing record - by definition a free ; seqnum. ;________________________________________________________________________________ ; AddAPPLCallback: proc with APPLLocals bset.b #applSawARecord, flags(a2) ; we've seen at least one record move.l APPLRec.TagInfo+Data(a2), theDate(a2) ; record leftmost record's create date move.w APPLKey.SeqNum+Key(a2), theSeqNum(a2) ; record leftmost record's sequence number move.l APPLRec.parID+Data(a2), d0 ; pick up the ParID cmp.l AppSpec.parID(a2), d0 ; entry in the same folder? <19> bne.s @NotADuplicate bsr APPLCompareNames ; are the names the same? bne.s @NotADuplicate ; no -> ; The current application record has the same creator, parID and name as the app ; being added. move.l APPLRec.TagInfo+Data(a2), d0 ; get this application's creation date cmp.l crDate(a2), d0 ; check it against our prospective entry bne.s @DifferentCreateDate ; handle same app/different date case bset.b #applFoundADup, flags(a2) ; indicate our find moveq.l #0, d0 ; signal the end of this search bra.s @Exit ; We've got most of a duplicate entry - everything except the create date matches. ; This really shouldn't happen, but if it does, simply return the seqnum of the ; current record as the place we should insert the new application. @DifferentCreateDate: move.w APPLKey.SeqNum+Key(a2), index(a2) ; use this seqnum bra.s @FoundASeqNum @NotADuplicate: btst.b #applFoundASeqNum, APPLLocals.flags(a2) ; do we need to find a seqnum? bne.s @KeepIterating move.w APPLKey.SeqNum+Key(a2), d0 ; get the current record's seqnum cmp.w index(a2), d0 ; collision? bne.s @FoundASeqNum ; if not, we've found a good seqnum sub.w #1, index(a2) ; subtract to get next prospective seqnum bra.s @KeepIterating @FoundASeqNum: bset.b #applFoundASeqNum, flags(a2) ; indicate our find @KeepIterating: moveq.l #1, d0 ; indicate that we should keep iterating @Exit: rts endwith _DTDebugTail 'AddAPPLCallback',0 endproc ;________________________________________________________________________________ ; Routine: DTAddAPPL ; ; Input: a0 - user's DT param block ; Output: d0 - result code ; ; Register Use: a0 - bt param block ; a1 - ; a2 - locals ; a3 - DTDBQElt ; a4 - user's param block ; ; Function: Add an entry for an application into the application list ; ; Add the given application to the app list. If this application is younger than ; all existing applications, make sure its sequence number is lowest. If it's not ; the youngest, add the application at the highest available seqnum. ; If the application has the same name, directory and tag as an entry in the list, ; no action is necessary. If the application has the same name and directory but a ; different tag, delete the existing entry and insert the application as if there ; had been no duplicate. ;________________________________________________________________________________ DTAddAPPL: proc export @AddAPPLRegs reg a0-a4/d1-d2 ; set of regs to save around this call jsrROM FSQUEUE ; just to check for external file systems bsr DTRfnExtFSCheck ; find the right volume and hope it's ours bsr DeskMgrQueue ; Wait our turn with APPLLocals ; Key and Data are APPL-sized movem.l @AddAPPLRegs, -(a6) suba.w #LSize, a6 ; allocate locals movea.l a6, a2 ; setup a2 as locals ptr movea.l a0, a4 ; move user's DTParam to a safe register bsr DTSetup ; Do common setup. a3 = ptr(DTDBQElt) bne @Exit2 ; Punt on any errors lea.l bigPB(a2), a0 ; a0 = ptr(param block) move.l ioFileName(a4), ioFileName(a0) ; use user's application name move.w DTDBQElt.XVRefNum(a3), ioVRefNum(a0); use correct volume ref clr.w ioFDirIndex(a0) ; no indexing move.l ioDirID(a4), ioDirID(a0) ; use user's DirID go_HGetFileInfo ; Go get this app's creation date <21> bne @Exit2 ; run away on errors move.l ioFlCrDat(a0), APPLLocals.crDate(a2) ; save the creation date move.l ioDirID(a4), ioDirID(a0) ; reset dirID field (modified by GetCatInfo) lea.l AppSpec(a2), a1 ; address of our FSSpec record move.l a1, ioFSSpecPtr(a0) ; tell the param block about it go_MakeFSSpec bne @Exit2 ; run away on errors move.w #$ffff, index(a2) ; initial seqnum is ffff to maintain compatibility clr.b flags(a2) ; clear all iterator signal flags move.l ioFlCreator(a4), d1 ; d1 = creator type for new APPL list entry bsr SetUpAPPLIterator ; get ready to iterate through existing entries lea.l AddAPPLCallback, a1 ; a1 = ptr(AddAPPL's iterator callback) bsr APPLIterator ; go look at each existing record move.w index(a2), d1 ; set up free seqnum btst.b #applSawARecord, flags(a2) ; did we see at least one good record? beq.s @Insert ; if not, insert the new sole record @SawAnExistingRecord: btst.b #applFoundADup, APPLLocals.flags(a2) ; did we find a duplicate? bne.s @NoErrExit ; if so, take the easy way out ; We have a valid seqnum. There's at least one other record with this creator, ; so make sure that the leftmost record has the newest creation date. ; There are four possibilities here, based on two facts: ; The free seqnum is left or right of the leftmost existing record ; The leftmost existing record is newer or older (by creation date) than the app being added ; ; free seqnum right of existing leftmost free seqnum left of existing leftmost ; caller's app newer: swap insert at free seqnum ; caller's app older: insert at free seqnum swap ; ; A swap puts the existing leftmost record into the free seqnum and the caller's ; app into the newly available seqnum where the leftmost app used to be. Note that ; this operation either ensures that the leftmost record stays that way (by moving it ; to a new, even lower seqnum) or that the new record becomes the leftmost (when the ; free seqnum was off to the right). ; ; This ensures that the leftmost record is always the one with the youngest app. @HaveASeqNum: move.l theDate(a2), d0 ; d0 = creation date of leftmost existing record cmp.l crDate(a2), d0 ; compare that to the app's creation date bhi.s @ExistingRecIsNewer move.w theSeqNum(a2), d0 ; d0 = seqnum of the leftmost existing record cmp.w index(a2), d0 ; compare that to this app's assigned seqnum blo.s @DoASwap ; left seq < free seq, left rec older than caller's bra.s @Insert ; left seq > free seq, left rec older than caller's @ExistingRecIsNewer: move.w theSeqNum(a2), d0 ; d0 = seqnum of leftmost existing record cmp.w index(a2), d0 ; compare that to this app's assigned seqnum blo.s @Insert ; left seq < free seq, left rec newer than caller's ; Move the existing leftmost record to the newly selected seqnum and the ; caller's app at the (freed up) seqnum of the existing leftmost ; record. We know that the iterator left us one to the left of the leftmost ; record. @DoASwap: move.l ioFlCreator(a4), APPLKey.CrType+Key(a2) ; iterator always bumps into previous creator type move.w theSeqNum(a2), APPLKey.SeqNum+Key(a2) ; we want the leftmost record go_BTSearch ; get the leftmost record move.w index(a2), APPLKey.SeqNum+Key(a2) ; use the selected free seqnum go_BTSetRec ; move the leftmost record into the list at the free spot move.w theSeqNum(a2), d1 ; and add the new record at the leftmost position ; Add the caller's application at the sequence number in d1 @Insert: lea.l APPLKey.Len+Key(a2), a1 ; a1 = ptr(APPL key) move.b #APPLKey.AKLen-1, (a1)+ ; make a full length APPL key (-1 for length byte) move.b #APPLKeyType, (a1)+ ; make it an APPL key move.l ioFlCreator(a4), (a1)+ ; use the caller's creator move.w d1, (a1)+ ; use the seqnum in d1 lea.l APPLRec.TagInfo+Data(a2), a1 ; a1 = ptr(APPL record) move.l crDate(a2), (a1)+ ; use this app's creation date move.l AppSpec+FSSpec.parId(a2), (a1)+ ; and parent ID lea.l AppSpec+FSSpec.name(a2), a0 ; a0 = ptr(CName of app) moveq.l #0, d0 ; clear high bytes move.b (a0), d0 ; name length into d0 (+1 for itself, -1 for dbra) @loop: move.b (a0)+, (a1)+ dbra d0, @loop lea.l bigPB(a2), a0 ; get our param block pointer back go_BTSetRec ; all fields are still valid bra.s @Exit ; return the error straight back @NoErrExit: moveq.l #0, d0 @Exit: bsr SaveHintInDTDBQElt ; save off the hint @Exit2: add.w #LSize, a6 ; deallocate locals movem.l (a6)+, @AddAPPLRegs bra DTDone ; We're all set. endwith _DTDebugRts 'DTAddAPPL',0 endproc ;________________________________________________________________________________ ; Routine: RemoveAPPLCallback ; ; Input: a0 - BT param block with valid hint, key, and data pointers ; a1 - callback procedure ; a2 - standard APPL locals ; a3 - DTDBQElt pointer ; d1 - creator type ; ; Output: a0 - BT param block ; a1 - callback procedure ; a2 - standard locals (param block, key, data) ; a3 - DTDBQElt pointer ; d0 - zero to signal end of iteration; nonzero otherwise ; d1 - creator type ; ; Function: ; This routine will get called once per APPL entry with a creator matching the ; one of the application being removed to the list. ; It does these things: ; Look for an entry whose name and dirID match the caller's pb ; If there's a match, find out if it is the leftmost entry ; Keep track of the youngest non-matching app seen, in case ; the matching app was leftmost and the youngest one needs to ; be found and made leftmost. ; ;________________________________________________________________________________ ; RemoveAPPLCallback: proc with APPLLocals add.w #1, count(a2) ; tally this record btst.b #applFoundAMatch, flags(a2) ; have we already seen a match? beq.s @NoMatchYet ; if not, keep on looking bset.b #applNotLeftmost, flags(a2) ; we've seen a match, and now another ; record, which means that the match ; wasn't the leftmost (youngest) entry, ; so no rebalancing will be necessary. moveq.l #0, d0 ; Tell the iterator to chill out bra.s @Exit ; And we're done iterating @NoMatchYet: move.l APPLRec.parID+Data(a2), d0 ; pick up the ParID cmp.l AppSpec.parID(a2), d0 ; entry in the same folder? <19> bne.s @NotAMatch bsr APPLCompareNames ; are the names the same? bne.s @NotAMatch ; no -> ; The current application record has the same creator, parID and name as the app ; being added. bset.b #applFoundAMatch, flags(a2) ; remember that we've found it move.w APPLKey.SeqNum+Key(a2), index(a2) ; remember its SeqNum bra.s @KeepIterating ; and go look for a more-leftmost entry ; The current entry is a miss. Check to see if it is the "newest" to date in the ; scan @NotAMatch: move.l theDate(a2), d0 ; date of the current "youngest" cmp.l APPLRec.TagInfo+Data(a2), d0 ; is this record younger? bhi.s @KeepIterating ; if not, just keep going move.w APPLKey.SeqNum+Key(a2), theSeqNum(a2) ; remember this SeqNum move.l APPLRec.TagInfo+Data(a2), theDate(a2) ; remember this date @KeepIterating: moveq.l #1, d0 ; tell the iterator to keep at it @Exit: rts endwith _DTDebugTail 'RemoveAPPLCallback',0 endproc ;________________________________________________________________________________ ; Routine: DTRemoveAPPL ; ; Input: a0 - user's DT param block ; Output: d0 - result code ; ; Register Use: a0 - bt param block ; a1 - ; a2 - locals ; a3 - DTDBQElt ; a4 - user's param block ; ; Function: Delete an entry for an application in the application list ; ; Remove the given application to the app list. If this application is younger than ; all existing applications, make sure that after it is deleted the next-youngest ; app is promoted to the front of the list. ;________________________________________________________________________________ DTRemoveAPPL: proc export @RemoveAPPLRegs reg a0-a4/d1-d2 jsrROM FSQUEUE ; just to check for external file systems bsr DTRfnExtFSCheck ; find the right volume and hope it's ours bsr DeskMgrQueue ; wait our turn with APPLLocals movem.l @RemoveAPPLRegs, -(a6) suba.w #LSize, a6 ; allocate locals movea.l a6, a2 ; set up a2 as locals ptr movea.l a0, a4 ; move user's DTParam to a safe register bsr DTSetup ; Do common setup. a3 = ptr(DTDBQElt) bne @Exit2 ; Punt on any errors lea.l bigPB(a2), a0 ; a0 = ptr(param block) move.l ioFileName(a4), ioFileName(a0) ; use user's application name move.w DTDBQElt.XVRefNum(a3), ioVRefNum(a0); use correct volume ref move.l ioDirID(a4), ioDirID(a0) ; use user's DirID lea.l AppSpec(a2), a1 ; address of our FSSpec record move.l a1, ioFSSpecPtr(a0) ; tell the param block about it go_MakeFSSpec beq.s @1 ; no err? How pleasant. cmp.w #fnfErr, d0 ; app doesn't have to be here bne.s @Exit2 ; other errors spell trouble @1: clr.b flags(a2) ; clear all iterator signal flags clr.l theDate(a2) ; oldest possible create date clr.w theSeqNum(a2) ; indicate no younger APPL in the list clr.w count(a2) ; we've seen no records move.l ioFlCreator(a4), d1 ; d1 = creator type for APPL entry to delete bsr SetUpAPPLIterator ; get ready to look through existing entries lea.l RemoveAPPLCallback, a1 ; a1 = ptr(RemoveAPPL's iterator callback) bsr APPLIterator ; go look at existing records btst.b #applFoundAMatch, flags(a2) ; did we see a hit? beq.s @NoMatchFound move.l ioFlCreator(a4), APPLKey.CrType+Key(a2) ; put our creator back into key ; We saw a hit. There are several things to consider, in this order ; 1) If there are only one or two records, we never need to re-balance ; 2) If the hit wasn't leftmost, we don't need to re-balance ; 3) If the hit was the leftmost record, we need to re-balance. cmp.w #2, count(a2) ; did we see more than 2 records? bls.s @DeleteMatch ; if not, just go delete the hit btst.b #applNotLeftmost, flags(a2) ; was the hit leftmost? bne.s @DeleteMatch ; if not, just go delete it ; We need to rebalance. Since we know the seqnums of the youngest record ; and the seqnum of the leftmost record, we'll just get the youngest and ; set it into the leftmost position. move.w theSeqNum(a2), APPLKey.SeqNum+Key(a2) ; go_BTSearch ; get the youngest bne.s @Exit ; errors are trouble move.w index(a2), APPLKey.SeqNum+Key(a2) ; leftmost SeqNum go_BTReplRec ; replace match with youngest bne.s @Exit ; errors are trouble move.w theSeqNum(a2), index(a2) ; aim our upcoming delete at ; the old position of the youngest @DeleteMatch: move.w index(a2), APPLKey.SeqNum+Key(a2) go_BTDelRec ; delete the match (or the old ; position of the youngest) bra.s @Exit ; pass errors straight back @NoMatchFound: move.l #btRecNotFnd, d0 ; tell'em about it and give up @Exit: bsr SaveHintInDTDBQElt ; save off the hint <21> @Exit2: adda.w #LSize, a6 ; deallocate locals movem.l (a6)+, @RemoveAPPLRegs ; restore registers bra DTDone ; We're all set. endwith _DTDebugRts 'DTRemoveAPPL',0 endp ;________________________________________________________________________________ ; ; Routine: DTGetAPPL ; ; Input: a0 points to user's DTParam ; Output: d0 contains result code ; ; Function: Retrieve an APPL entry from the Desktop database by Creator/index ;________________________________________________________________________________ DTGetAPPL: proc export StandardLocals GetAPPLLocals, APPLRec @GetAPPLRegs reg a0-a4/d1-d2 ; set of regs to save around GetAPPL jsrROM FSQUEUE ; just to check for external file systems bsr DTRfnExtFSCheck ; find the right volume and hope it's ours bsr DeskMgrQueue ; Wait our turn with GetAPPLLocals ; Key and Data refer to locals movem.l @GetAPPLRegs, -(a6) suba.w #LSize, a6 ; allocate locals on a6 movea.l a0, a4 ; move user's DTParam to a safe register bsr DTSetup ; Do common setup. a3 = ptr(DTDBQElt) bne @GetAPPLExit2 ; and run if we see trouble bsr SetUpBTPB ; a0 = ptr(BTioParam), a2 = ptr(key) move.l #APPLRec.ARLen, ioReqCount(a0) ; indicate size of an APPL data record move.b #APPLKey.AKLen-1, (a2)+ ; set APPL key length (minus length byte) move.b #APPLKeyType, (a2)+ ; and APPL type move.l ioFlCreator(a4), (a2)+ ; look for user's creator clr.l (a2)+ ; and a value "left" of all seqnums go_BTSearch cmp.w #btRecNotFnd, d0 ; was the error a simple not found? bne.s @GetAPPLExit ; no, must be something more serious move.w ioIndex(a4), d0 ; get the user's requested index tst.w d0 ; is index 0? bne.s @notSpecial ; if not, don't adjust moveq.l #1, d0 ; caller wants "best" app, which happens to be 1st one @notSpecial: subq.w #1, d0 ; the user's index starts at 1 move.w d0, ioBTPosMode(a0) ; tell the btree the record offset move.w #APPLKey.AKLen, ioKReqCount(a0) ; tell the btree how much space for key return go_BTGetRec bne.s @GetAPPLExit ; Punt on errors move.b APPLKey.Type+Key(a6), d0 ; pick up the key ID cmp.b #APPLKeyType, d0 ; is this record an APPL entry? beq.s @FoundAPPL ; yes - check the creators ; move.w #btRecNotFnd, d0 ; no - search failed bra.s @GetAPPLExit ; So punt @FoundAPPL: ; ; move.l APPLKey.CrType+Key(a6), d0 ; pick up this APPL entries' creator ; cmp.l ioFlCreator(a4), d0 ; is it still the same as the caller's? ; beq.s @FoundOne ; yes - return information ; move.w #btRecNotFnd, d0 ; no - search failed ; bra.s @GetAPPLExit ; So punt ; @FoundOne: move.l APPLRec.TagInfo+Data(a6), ioTagInfo(a4) ; return tag information move.l APPLRec.parID+Data(a6), ioFlParID(a4) ; return ParID tst.l ioFileName(a4) ; did the user want the file name? beq.s @NoCopy ; no - don't copy it over lea.l APPLRec.CName+Data(a6), a0 ; a0 = ptr(the Cname string) movea.l ioFileName(a4), a1 ; a1 = ptr(user's Cname string) moveq.l #0, d0 ; clear high bytes move.b (a0), d0 ; get length addq.b #1, d0 ; add one for the length byte _BlockMove ; copy the string over @NoCopy: moveq.l #0,d0 ; All is well. @GetAPPLExit: bsr DoneBTPB ; finish up with the BTPB <21> @GetAPPLExit2: adda.w #LSize, a6 ; deallocate locals movem.l (a6)+, @GetAPPLRegs bra DTDone ; We're all set. endwith _DTDebugRts 'DTGetAPPL',0 endp ;________________________________________________________________________________ ; ; Routine: DTSetComment ; ; Input: a0 points to user's DTParam ; Output: d0 contains result code ; ; Function: Add a comment for a file or folder to the Desktop database ;________________________________________________________________________________ DTSetComment: proc export StandardLocals SetCommentLocals, CommentRec @SetCommentRegs reg a0-a4/d1-d2 ; set of regs to save around SetComment jsrROM FSQUEUE ; just to check for external file systems bsr DTRfnExtFSCheck ; find the right volume and hope it's ours bsr DeskMgrQueue ; Wait our turn with SetCommentLocals ; Key and Data refer to locals movem.l @SetCommentRegs, -(a6) suba.w #LSize, a6 ; allocate locals on a6 movea.l a0, a4 ; move user's DTParam to a safe register bsr DTSetup ; Do common setup. a3 = ptr(DTDBQElt) bne.s @AddCmtExit ; exit, stage left, if, like, errors are happenin bsr CheckCSpec ; Make sure the file or directory exists bne.s @AddCmtExit ; Punt on errors ; ; Build a Comment data record. Notice that since we build the data record at Data(a6), ; SetUpBTPB will automatically point the BTParam at it. ; moveq.l #0, d0 ; clear high bytes (for _BlockMove) move.l ioReqCount(a4), d0 ; get comment text length cmp.l #MaxCommentLen, d0 ; is the comment too large? bls.s @NoClip ; if not, no need to clip it move.l #MaxCommentLen, d0 ; clip length to max @NoClip: move.b d0, CommentRec.CmtSize+Data(a6) ; set the comment length move.l ioBuffer(a4), a0 ; get ready to copy the user's data lea.l CommentRec.CmtData+Data(a6), a1 ; into the Comment record _BlockMove ; copy that comment... bsr SetUpBTPB ; a0 = ptr(BTioParam), a2 = ptr(key) moveq.l #0, d0 ; clear high bytes move.b CommentRec.CmtSize+Data(a6), d0 ; d0 = size of comment data addq.b #1, d0 ; add one for length byte move.l d0, ioReqCount(a0) ; tell BTParam how big this comment record is move.b #CommentKey.CKLen-1, (a2)+ ; set key length (minus length byte) move.b #CommentKeyType, (a2)+ ; set key type move.l d1, (a2)+ ; d1 still has CNodeID from CheckCSpec go_BTSetRec bsr DoneBTPB ; finish up with the BTPB <21> @AddCmtExit: adda.w #LSize, a6 ; dispose of locals movem.l (a6)+, @SetCommentRegs ; restore regs bra DTDone ; We're all set. endwith _DTDebugRts 'DTSetComment',0 endproc ;________________________________________________________________________________ ; ; Routine: DTGetComment ; ; Input: a0 points to user's DTParam ; Output: d0 contains result code ; ; Function: Retrieve a comment for a file or folder from the Desktop database ;________________________________________________________________________________ DTGetComment: proc export StandardLocals GetCommentLocals, CommentRec @GetCommentRegs reg a0-a4/d1-d2 ; set of regs to save around GetComment jsrROM FSQUEUE ; just to check for external file systems bsr DTRfnExtFSCheck ; find the right volume and hope it's ours bsr DeskMgrQueue ; Wait our turn with GetCommentLocals ; Key and Data refer to locals movem.l @GetCommentRegs, -(a6) suba.w #LSize, a6 ; allocate locals on a6 movea.l a0, a4 ; move user's DTParam to a safe register bsr DTSetup ; Do common setup. a3 = ptr(DTDBQElt) bne.s @GetCommentExit ; Errors? Run and hide. clr.l ioActCount(a4) ; in case we don't make it bsr CheckCSpec ; Look up the CNode's CNID bne.s @GetCommentExit ; punt on errors @GCGotCNID: bsr SetUpBTPB ; a0 = ptr(BTioParam), a2 = ptr(key) move.l #CommentRec.CRLen, ioReqCount(a0) ; indicate size of a comment record move.b #CommentKey.CKLen-1, (a2)+ ; set the key length (minus length byte) move.b #CommentKeyType, (a2)+ ; and the type move.l d1, (a2)+ ; d1 = CNodeID of requested file (from CheckCSpec) go_BTSearch ; go get that comment bsr DoneBTPB ; finish up with the BTPB <21> bne.s @GetCommentExit ; Yipes! an error! moveq.l #0, d0 ; clear high bytes move.b CommentRec.CmtSize+Data(a6), d0 ; d0 = length of comment data cmp.b #MaxCommentLen, d0 ; for some strange reason, is comment too big? bls.s @sizeOK move.b #MaxCommentLen, d0 ; clip it. @sizeOK: move.l d0, ioActCount(a4) ; tell the user how much data there was lea.l CommentRec.CmtData+Data(a6), a0 ; copy starts from the Data move.l ioBuffer(a4), a1 ; a1 = address of user's buffer _BlockMove ; put the Comment Data in the user's buffer moveq.l #0,d0 ; All went well @GetCommentExit: adda.w #LSize, a6 ; deallocate locals movem.l (a6)+, @GetCommentRegs bra DTDone ; We're all set. endwith _DTDebugRts 'DTGetComment',0 endproc ;________________________________________________________________________________ ; ; Routine: DTRemoveComment ; ; Input: a0 points to user's DTParam ; Output: d0 contains result code ; ; Function: Remove a comment for a file or folder from the Desktop database ;________________________________________________________________________________ ; DTRemoveComment: proc export StandardLocals RemoveCommentLocals, 0 ; we only need a pb and a key here. Zero Data @RemoveCommentRegs reg a0-a4/d1-d2 ; set of regs to save around RmvCmt jsrROM FSQUEUE ; just to check for external file systems bsr DTRfnExtFSCheck ; find the right volume and hope it's ours bsr DeskMgrQueue ; Wait our turn with RemoveCommentLocals ; Key and Data refer to locals movem.l @RemoveCommentRegs, -(a6) ; save regs suba.l #LSize, a6 ; allocate and clear locals on a6 movea.l a0, a4 ; move user's DTParam to a safe register bsr DTSetup ; Do common setup. a3 = ptr(DTDBQElt) bne.s @RmvCmtExit ; Zorro defects bsr CheckCSpec ; does the file exist? (d1 = CNodeID) bne.s @RmvCmtExit ; punt on errors bsr SetUpBTPB ; a0 = ptr(BTparam), a2 = ptr(key) move.b #CommentKey.CKLen-1, (a2)+ ; set key length (minus length byte) move.b #CommentKeyType, (a2)+ ; it's a comment key move.l d1, (a2)+ ; d1 = CNodeID of file to delete comment for go_BTDelRec bsr DoneBTPB ; finish up with the BTPB <21> @RmvCmtExit: adda.w #LSize, a6 ; deallocate locals movem.l (a6)+, @RemoveCommentRegs bra DTDone ; We're all set. endwith _DTDebugRts 'RmvComment',0 endp ;________________________________________________________________________________ ; ; Routine: DTFlush ; ; Input: a0 points to user's DTParam ; Output: d0 contains result code ; ; Function: Flush buffers associated with a database ;________________________________________________________________________________ DTFlush: proc export DTFlushLocals record 0, increment bigPB ds.b DTBigPBSize ; big enough for all HFS calls LSize equ *-bigPB endr @DTFlushRegs reg a0/a3-a4 jsrROM FSQUEUE ; just to check for external file systems bsr DTRfnExtFSCheck ; find the right volume and hope it's ours bsr DeskMgrQueue ; wait our turn with DTFlushLocals movem.l @DTFlushRegs, -(a6) ; save regs suba.w #LSize, a6 ; allocate locals on file system stack movea.l a0, a4 ; move user's DTParam to a safe register bsr DTSetup ; Do common setup. a3 = ptr(DTDBQElt) bne.s @DTFlushExit ; punt on errors lea.l bigPB(a6), a0 ; a0 = ptr(param block) move.w DTDBQElt.DBRefNum(a3), ioRefNum(a0) ; use the btree file refnum go_BTFlush bne.s @DTFlushExit ; punt on errors lea.l bigPB(a6), a0 ; a0 = ptr(param block) move.w DTDBQElt.VRefNum(a3), ioVRefNum(a0) ; use the data file refnum clr.l ioVNPtr(a0) ; no name needed <21> go_FlushVol ; flush a volume at a time <21> @DTFlushExit: adda.w #LSize, a6 movem.l (a6)+, @DTFlushRegs bra DTDone endwith _DTDebugRts 'DTFlush',0 endproc ;________________________________________________________________________________ ; ; Routine: DTReset ; ; Input: a0 points to user's DTParam ; Output: d0 contains result code ; ; Function: Reinitialize the btree files (leaving them both open) ; SetEOF on both to zero ; Call BTInit on the BTree file ;________________________________________________________________________________ DTReset: proc export DTResetLocals record 0, increment bigPB ds.b DTBigPBSize ; big enough for all HFS calls DBName ds.b 32 ; holds btree file name DBParID ds.l 1 ; ParID of btree file LSize equ *-bigPB endr @DTResetRegs reg a0-a4/d1-d2 jsrROM FSQUEUE ; just to check for external file systems bsr DTRfnExtFSCheck ; find the right volume and hope it's ours bsr DeskMgrQueue ; wait our turn with DTResetLocals movem.l @DTResetRegs, -(a6) ; save regs suba.w #LSize, a6 ; allocate locals on file system stack movea.l a0, a4 ; move user's DTParam to a safe register bsr DTSetup ; Do common setup. a3 = ptr(DTDBQElt) bne @DTResetExit ; and punt on errors ; ; Get the name and parent ID of the btree file ; lea.l bigPB(a6), a0 ; a0 = ptr(param block) lea.l DBName(a6), a1 ; a1 = ptr(space for btree file name) move.l a1, ioFileName(a0) ; tell the param block about it clr.w ioVRefNum(a0) ; look at all FCB's move.w DTDBQElt.DBRefNum(a3), ioRefNum(a0) ; look for the btree file clr.l ioFCBIndx(a0) ; and look only by ioRefNum go_GetFCBInfo bne @DTResetExit move.l ioFCBParID(a0), DBParID(a6) ; stash btree's parID ; ; Free up all of the space in both files ; lea.l bigPB(a6), a0 ; a0 = ptr(param block) move.w DTDBQElt.DBRefNum(a3), ioRefNum(a0) ; zap the btree file clr.l ioLEOF(a0) ; deallocate all space go_SetEOF bne @DTResetExit ; errors are serious trouble here lea.l bigPB(a6), a0 ; a0 = ptr(param block) move.w DTDBQElt.DFRefNum(a3), ioRefNum(a0) ; zap the data file clr.l ioLEOF(a0) ; deallocate all space go_SetEOF bne @DTResetExit ; errors are serious trouble here ; ; <21> To minimize fragmentation, we extend the icon datafile by a bunch each time ; move.l DTDBQElt.DFClumpSize(a3), ioReqCount(a0) ; d1 = the data file's clump size go_Allocate ; Request the additional space ; ; Close the btree file so we can BTInit it ; lea.l bigPB(a6), a0 ; a0 = ptr(param block) move.w DTDBQElt.DBRefNum(a3), ioRefNum(a0) go_BTClose bne.s @DTResetExit ; ; Now call BTInit on the btree file ; lea.l bigPB(a6), a0 ; a0 = ptr(param block) lea.l DBName(a6), a1 ; a1 = ptr(DB file name) move.l a1, ioFileName(a0) ; use btree file name move.w DTDBQElt.VRefNum(a3), ioVRefNum(a0) ; use the volume the file was on move.w #DTMaxKeySize, ioBTMaxKLen(a0) ; set maximum key size clr.l ioBTClumpSize(a0) ; use volume clump size move.l DBParID(a6), ioDirID(a0) ; use DirID from when it was open pea.l DTKeyDescriptor ; push pointer to our key descriptor move.l (sp)+, ioBTKDPtr(a0) ; and tell the btree about it go_BTInit ; initialize btree file bne.s @DTResetExit ; ; And then open it back up ; lea.l bigPB(a6), a0 ; a0 = ptr(param block) lea.l DBName(a6), a1 ; a1 = ptr(DB file name) move.l a1, ioFileName(a0) ; use the name it had when it was open move.w DTDBQElt.VRefNum(a3), ioVRefNum(a0) ; use vRef from when it was open move.b #fsRdWrPerm, ioPermssn(a0) ; use exclusive read/write perm move.l DBParID(a6), ioDirID(a0) ; use DirID from when it was open pea.l DTKeyCmp ; use our own key comparison procedure move.l (sp)+,ioBTKCProc(a0) go_BTOpen bne @DTResetExit ; punt if there's trouble move.w ioRefNum(a0), DTDBQElt.DBRefNum(a3) ; save away the btree file refnum moveq.l #noErr, d0 ; we made it. @DTResetExit: adda.w #LSize, a6 movem.l (a6)+, @DTResetRegs bra DTDone endwith _DTDebugRts 'DTReset',0 endproc ;________________________________________________________________________________ ; Routine: DTDelete ; ; Input: a0 points to param block with ioVRefNum ; Output: d0 contains result code ; ; Function: Delete all files that make up the desktop database ; Note that DTReset only works if the desktop database can be ; opened, while DTDelete takes a volume refnum ;________________________________________________________________________________ DTDelete: proc export DTDeleteLocals record 0, increment bigPB ds.b DTBigPBSize ; big enough for all HFS calls DBName ds.b 32 ; holds btree file name DBParID ds.l 1 ; ParID of btree file LSize equ *-bigPB endr @DTDeleteRegs reg a0-a4/d1-d2 jsrROM FSQUEUE ; just to check for external file systems bsr DTVolExtFSCheck ; find the right volume and hope it's ours bsr DeskMgrQueue ; wait our turn with DTDeleteLocals movem.l @DTDeleteRegs, -(a6) ; save regs suba.w #LSize, a6 ; allocate locals on file system stack movea.l a0, a4 ; move user's DTParam to a safe register move.l FSVarsPtr, a3 ; a3 = ptr(file system global area) move.l FSVars.DTDBMgr(a3), a3 ; a3 = ptr(DTDB manager's global area) move.w DTGlobals.targetVRef(a3), d0 ; get caller's target volume move.w d0, d2 ; save a copy bsr FindDTVol ; is this volume's database already open? beq.s @DatabaseOpenExit ; if so, we can't delete the files lea.l DBName(a6), a1 ; a1 = ptr(space for database file name) move.w d2, d0 ; d0 = vRef bsr FindDTFiles ; go look for the database files ; note that we don't bother to look for errors here ; delete the btree file lea.l bigPB(a6), a0 move.l a1, ioNamePtr(a0) ; aim at the btree file move.w d1, ioVRefNum(a0) ; on the right volume move.l d2, ioDirID(a0) ; in the right directory go_HDelete ; ignore errors moveq.l #0, d0 ; clear high bytes move.b (a1), d0 ; get length of btree file name move.b #'F', (a1, d0.w) ; change name to datafile name go_HDelete ; ignore errors moveq.l #noErr, d0 ; we always "succeed" bra.s @DTDeleteExit @DatabaseOpenExit: moveq.l #fBsyErr, d0 ; can't delete if the database is open @DTDeleteExit: adda.w #LSize, a6 movem.l (a6)+, @DTDeleteRegs bra DTDone endwith _DTDebugRts 'DTDelete',0 endproc ;________________________________________________________________________________ ; Routine: DTGetInfo ; ; Input: a0 points to user's DTParam ; Output: d0 contains result code ; ; Function: Returns informative and entertaining facts about today's desktop ; ; ioFlLgLen gets the sum of the logical lengths of the desktop files ; ioFlPyLen gets the sum of the physical lengths of the desktop files ; ioDTFlCnt gets the number of desktop files ; ioDirID gets the parent directory of the desktop files ; ; Even though neither file normally has a resource fork, we compute them ; in in case someone has added one. ;________________________________________________________________________________ DTGetInfo: proc export GetDTILocals record 0, increment bigPB ds.b DTBigPBSize ; big enough for all HFS calls CName ds.b 32 ; space for a name LSize equ *-bigPB endr @DTGetInfoRegs reg a0-a4/d1-d2 jsrROM FSQUEUE ; just to check for external file systems bsr DTRfnExtFSCheck ; find the right volume and hope it's ours bsr DeskMgrQueue ; wait our turn with GetDTILocals movem.l @DTGetInfoRegs, -(a6) ; save regs suba.w #LSize, a6 ; allocate locals on file system stack movea.l a0, a4 ; move user's DTParam to a safe register bsr DTSetup ; Do common setup. a3 = ptr(DTDBQElt) bne.s @DTGetInfoExit ; and punt on errors lea.l bigPB(a6), a0 ; a0 = ptr(param block) lea.l CName(a6), a1 ; space for a name move.l a1, ioNamePtr(a0) ; tell 'em where to put the name clr.l ioDTLgLen(a4) ; clear out so we can add <29> clr.l ioDTPyLen(a4) ; <29> move.w DTDBQElt.DBRefNum(a3), ioRefNum(a0) ; use the btree file bsr.s @AddFileSizes bne.s @DTGetInfoExit move.w DTDBQElt.DFRefNum(a3), ioRefNum(a0) ; use the data file bsr.s @AddFileSizes bne.s @DTGetInfoExit move.w #2, ioDTFlCnt(a4) ; indicate # of desktop files move.w DTDBQElt.VRefNum(a3), ioVRefNum(a4) ; indicate volume with files moveq.l #0, d0 ; we succeeded <29> @DTGetInfoExit: adda.w #LSize, a6 movem.l (a6)+, @DTGetInfoRegs bra DTDone ; assumes ; a0 - param block with refnum and name pointer set up ; a4 - user dt param block ; @AddFileSizes: move.l (sp)+, -(a6) ; <18> clr.l ioFCBIndx(a0) ; return info by refnum only go_GetFCBInfo bne.s @scram move.l ioFCBParID(a0), ioDirID(a0) ; move for GetCatInfo call clr.w ioFDirIndex(a0) ; by name & vref go_HGetFileInfo ; <21> bne.s @scram move.l ioDTLgLen(a4), d0 ; get the current value <29> add.l ioFlLgLen(a0), d0 ; add in data fork logical length add.l ioFlRLgLen(a0), d0 ; add in the resource fork logical length move.l d0, ioDTLgLen(a4) ; stash it away <29> move.l ioDTPyLen(a4), d0 ; get the current value <29> add.l ioFlPyLen(a0), d0 ; add in data fork physical length add.l ioFlRPyLen(a0), d0 ; add in the resource fork physical length move.l d0, ioDTPyLen(a4) ; stash it away <29> moveq.l #0,d0 ; we succeeded <29> @scram: move.l (a6)+, -(sp) ; <18> tst.w d0 ; <29> rts ; rah endwith _DTDebugTail 'DTGetInfo',0 endproc ;________________________________________________________________________________ ; ; Routine: DTKeyCmp ; ; Function: Compare two Desktop B*-Tree keys. ; ; Input: A0.L - search key pointer ; A1.L - trial key pointer ; ; Output: D0.W - result code ; +n search key > trial key ; 0 search key = trial key ; -n search key < trial key ; ; NOTE: The keys in the Desktop B*-Tree are all either numeric or case-sensitive ; strings, differentiated in the first position by the key type. The key ; type will ensure that keys are sorted by type first; beyond that, a ; straight byte comparison may be used to properly differentiate the keys. ;________________________________________________________________________________ ; DTKeyCmp: proc MOVEM.L A0-A1/D1,-(SP) ; Save away scratch registers ; ; Figure the trial key length from the search key type; since all index ; keys are padded out to their full length, the trial key length is ; unreliable. The search key length cannot be used because Getxxx routines ; pass shorter keys (Type & Creator only) meant to compare less than any ; key by that creator, but greater than preceding Creators. ; MOVE.B (A0)+,D0 ; Pick up length of search key MOVEQ #IconKey.IKLen-1,D1 ; Figure length of icon key CMP.B #IconKeyType,(A0) ; Looking for an icon key? BEQ.S @5 ; Yes - we're in business MOVEQ #APPLKey.AKLen-1,D1 ; No - try an APPL key CMP.B #APPLKeyType,(A0) ; Looking for an APPL key? BEQ.S @5 ; Yes - go with it MOVEQ #CommentKey.CKLen-1,D1 ; Finally, try a comment key CMP.B #CommentKeyType,(A0) ; Looking at a comment? BEQ.S @5 ; Yes - finally right. MOVE.B D0,D1 ; No - just use the search key length ; @5 TST.B (A1)+ ; Ignore length of trial key @10 CMPM.B (A1)+,(A0)+ ; Compare byte in key BHI.S DTKCIsGT ; search key > trial key BLO.S DTKCIsLT ; search key < trial key SUBQ.B #1,D1 ; Count off one trial key character SUBQ.B #1,D0 ; Count off one search key character BEQ.S @20 ; If zero, stop comparison TST.B D1 ; Reached end of trial key? BNE.S @10 ; No - keep checking BRA.S DTKCIsGT ; Yes - search key > trial key @20 TST.B D1 ; Also reached end of trial key? BNE.S DTKCIsLT ; No - search key < trial key DTKCIsEQ MOVEQ #0,D0 ; The two keys are equal BRA.S DTKCExit ; So we're done DTKCIsLT MOVEQ #-1,D0 ; Search key < trial key BRA.S DTKCExit DTKCIsGT MOVEQ #1,D0 ; Search key > trial key DTKCExit MOVEM.L (SP)+,A0-A1/D1 ; Restore scratch registers [leaves CC alone] RTS ; And call it a day, leaving CC set from D0 ;________________________________________________________________________________ ; ; Routine: deepShitError ; ; Input: ; Output: ; ; Function: Called when an error that shouldn't happen has, and the system ; is in an inconsistent state. ; ; Called when our request for unmount notification fails. ; Called in an _enqueue fails in OpenDT ; Called if a _dequeue fails in CloseDT ;________________________________________________________________________________ deepShitError proc move.w #HFSDSErr, d0 _SysError ; bye-bye endproc end