mirror of
https://github.com/elliotnunn/supermario.git
synced 2024-11-26 16:49:18 +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
|