mirror of
https://github.com/elliotnunn/supermario.git
synced 2025-02-18 12:30:31 +00:00
3453 lines
130 KiB
Plaintext
3453 lines
130 KiB
Plaintext
|
;
|
|||
|
; 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):
|
|||
|
;
|
|||
|
; <SM4> 6/9/93 pdw Changed register saving from interruptRegs to pascalRegs in
|
|||
|
; completionRoutine because it is supposed to be using this
|
|||
|
; convention.
|
|||
|
; <SM3> 10/27/92 CSS Changed some word bsrs to bigjsrs.
|
|||
|
; <SM2> 10/27/92 CSS Changed short branch to word branch.
|
|||
|
; <SM1> 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. <dnf 15>)
|
|||
|
; <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' ; <SM3> 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
|
|||
|
;________________________________________________________________________________ <SM1>
|
|||
|
; <36>
|
|||
|
; Routine: DesktopCloseDownProc
|
|||
|
;
|
|||
|
; Inputs param block to _Unmount
|
|||
|
;
|
|||
|
; Outputs: (none)
|
|||
|
;
|
|||
|
; Function: Close the desktop database on _Unmount calls
|
|||
|
;
|
|||
|
;________________________________________________________________________________
|
|||
|
; Rolled into TFS.a for SuperMario <SM1> 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.
|
|||
|
;__________________________________________________________________________________ <SM1>
|
|||
|
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
|
|||
|
|
|||
|
; <SM1> FM needed for GetVolParms
|
|||
|
;________________________________________________________________________________ <SM1>
|
|||
|
;
|
|||
|
; 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 <SM3> CSS
|
|||
|
bigjsr GetQMRecPtr,a4 ; a1 = ptr(QMRec) <SM3> CSS
|
|||
|
move.l (sp)+,a4 ; recover a4 <SM3> 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 <SM3> CSS
|
|||
|
bigjsr GetQMRecPtr,a4 ; a1 = ptr(QMRec) <SM3> CSS
|
|||
|
move.l (sp)+,a4 ; recover a4 <SM3> 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 <SM4>pdw
|
|||
|
pea restorePascalRegs ; and get in the chain to restore them later <SM4>pdw
|
|||
|
|
|||
|
contDeskThread:
|
|||
|
moveq.l #desktopQType, d2 ; our queue type/refnum
|
|||
|
MACHINE mc68020
|
|||
|
move.l a4,-(sp) ; save a4 <SM3> CSS
|
|||
|
bigjsr GetQMRecPtr,a4 ; a1 = ptr(QMRec) <SM3> CSS
|
|||
|
move.l (sp)+,a4 ; recover a4 <SM3> 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: ; <SM4>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? <dnf 1.4>
|
|||
|
bne.s @notBtRecNotFnd ; no - go check the next error
|
|||
|
move.w #afpItemNotFound, d0 ; yes - then replace it with the right afp error<dnf 1.4>
|
|||
|
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 '<volume name> DDB' and '<volume name> 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 <SM2> 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) <dnf 1.3>
|
|||
|
_Dequeue
|
|||
|
tst.w d0 ; 'cause _Dequeue doesn't test <dnf 1.3>
|
|||
|
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 ; <dnf 1.4>
|
|||
|
move.w #btRecNotFnd, d0 ; no - search failed
|
|||
|
bra.s @GetAPPLExit ; So punt
|
|||
|
|
|||
|
@FoundAPPL: ; ; <dnf 1.4>
|
|||
|
move.l APPLKey.CrType+Key(a6), d0 ; pick up this APPL entries' creator ; <dnf 1.4>
|
|||
|
cmp.l ioFlCreator(a4), d0 ; is it still the same as the caller's? ; <dnf 1.4>
|
|||
|
beq.s @FoundOne ; yes - return information ; <dnf 1.4>
|
|||
|
move.w #btRecNotFnd, d0 ; no - search failed ; <dnf 1.4>
|
|||
|
bra.s @GetAPPLExit ; So punt ; <dnf 1.4>
|
|||
|
|
|||
|
@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
|