; ; File: ScrapMgr.a ; ; Contains: Scrap Manager for Macintosh Operating System ; ; Copyright: © 1983-1993 by Apple Computer, Inc., all rights reserved. ; ; Change History (most recent first): ; ; 5/12/93 PN Roll in Dean's changes. Cleaned up and shorten PutScrap patch ; roll in ; 12/21/92 PN Radar#1048596 Fix the return address on the stack of DoPut ; routine depending on _PtrAndHandl call ; 10/22/92 CSS Change some branch short instructions to word branches. ; 4/9/92 PN Roll in FixputScrap and F2PutScrap from ScrapMgrPatches.s ; <2> 2/10/92 JSM Moved this file to ScrapMgr folder, keeping all the old ; revisions; cleaned up header. ; <1.1> 11/10/88 CCH Fixed Header. ; <1.0> 11/9/88 CCH Adding to EASE. ; <¥1.1> 9/23/88 CCH Got rid of inc.sum.d and empty nFiles ; <1.0> 2/11/88 BBM Adding file for the first time into EASEÉ ; 1/28/87 bbm Fixed obscure error in unloadseg (now exits correctly.) ; 10/9/86 bbm Modified to mpw aincludes. ; 2/19/86 BBM Made some modifications to work under MPW ; 6/15/85 SC ZeroScrap will never let scrap count go negative. ; 6/15/85 SC InfoScrap will zero scrap if uninited too. ; 4/27/85 SC GetScrap and PutScrap will never return noScrap error. If the ; scrap hasn't been inited, it will call zero scrap for caller. ; New routine FailSafe used for this. ; 4/27/85 SC Set Type, Creator and system file bit in IOUsrWds for finder. ; 4/27/85 SC Scrap File moved to bootdrive, not default. ; 1/25/85 LAK Added patch in LoadScrap (pass D0=bytecount to SetUpRW2). ; 1/23/85 LAK Adapted for new equate files. ; 9/7/83 SC Added scrapCount ; 9/5/83 SC Andy found a bug... ; 8/29/83 SC Changed scrap info interface ; 8/21/83 SC Crunched eight bytes out ; ; ;--------------------------------------------------------------- ; ; Mac Scrap Manager ; ; This is a set of simple routines to help the applications ; manipulate the scrap for inter-application cutting and pasting. ; ; The scrap is simply a collection of typed data objects(Hi Bruce). ; in a handle that is maintained through application launches. ; There are two "Universal types," TEXT, for vanilla text, and PICT, ; for QuickDraw pictures. However, the application is free to ; add private types like SYLK. ; ; The general rules of thumb are: applications MUST be able to ; read both universal types and write at least one. This should ; insure that all applications can minimally exchange information ; through at least one level. If applications choose to write more ; than one type, the order is important; one should write the types ; in order of importance to that application. eg. a word processor ; should write out its private format first, then text, then a picture ; if desired. This implicitly states which type has the highest ; information content coming from that particular application. When ; reading the scrap one should scan for the types in the same order ; in an attempt to recover the highest information content. ; ; There are user-defined "fields" in both the TEXT and PICT types so ; the applications are encouraged to place custom data there in an ; effort to reduce the size of the scrap. As in the example above, a ; word processor might hide its formatting information in the TEXT ; comments so that only two types are written into the scrap. ; ; The script for using the scrap is: The application starts up. It ; inquires as early as possible about the scrap to determine if there ; will be enough room for the application and scrap to coexist in the ; heap. If not, it calls an unload procedure to save the scrap to disk. ; If that fails, or is deemed impossible, an alert should be raised to ; inform the user and ask for guidance. The user might just want to ; abort the application at this point. ; ; Then the application must remember that the universal scrap is ; pregnant with data when a paste command is executed. At that point ; it should dissect the scrap to find the most logical type and ; paste that into its data. An alternative is to automatically ; convert the scrap to the applications internal format so that the ; paste code can be dumb and not have to serve two masters. It is ; important to realize that the scrap data cannot be dumped until a ; cut/copy-like command is executed. While the applications runs ; there is, of course, no need to maintain or otherwise diddle with ; the universal scrap. ; ; When the application terminates, it should make every attempt to ; dump its scrap into the universal one. Obviously, if the application ; has a disk based scrap, it may be impossible to copy into memory. ; Or even with a memory based scrap, there might not be enough room ; for a "second" copy in the heap. In either case an alert should ; be raised telling the user of the applications dilemma. The user ; might at this point stay in the application and prune the scrap. ; ; When the user activates or deactivates an ornament, a similar course ; should be followed by the application. The activate and deactivate ; events contain some additional bits in the modifiers field which will ; indicate whether the transition is between a system or application ; window. If this case is true and the event is a deactivate, the ; application should call the same code as the termination sequence ; above. Of course, activate events should then parallel the startup ; scenario. ; ; ; The routines are: ; ; ScrapStuff = RECORD ; scrapSize: LONGINT; ; scrapHandle: Handle; ; scrapCount: INTEGER; ; scrapState: INTEGER; ; scrapName: StringPtr; ; END; ; pScrapStuff: ^ ScrapStuff; ; FUNCTION InfoScrap: pScrapStuff; ; returns a pointer to the scrap info ; ; FUNCTION UnloadScrap: LONGINT; ; Unloads the scrap from memory ; ; FUNCTION LoadScrap: LONGINT; ; Loads the scrap from scrap file into memory ; ; FUNCTION GetScrap( hDest: Handle; what: ResType; ; VAR offset: LONGINT ): LONGINT; ; hDest = the destination handle in which to copy the object. ; If handle is NIL, then doesn't get it. ; what = the type of object to copy ; offset returned as offset in scrap of this object ; result is -1 if it couldn't be found or a negative system ; error; set >= 0 (to size) if OK ; ; FUNCTION ZeroScrap: LONGINT; ; Cuts back the scrap to empty. ; ; FUNCTION PutScrap( length: LONGINT; what: ResType; source: Ptr ): LONGINT; ; what = the type of object to copy ; source = pointer from which to copy the object ; length = length of object at source ; BLANKS ON STRING ASIS LOAD 'StandardEqu.d' ; 3 States of scrap (In scrapState): negative uninitialized, positive on disk, ; zero in memory ; Defs for a scrap entry scrType EQU 0 scrLength EQU scrType+4 scrData EQU scrLength+4 ; Std frame off A6 scrapIO EQU -IOFQElSize ; io cmd dLength EQU scrapIO-4 ; disk object length dType EQU dLength-4 ; disk object type stdLink EQU dType ; std frame size ScrapMgr PROC EXPORT EXPORT InfoScrap EXPORT LoadScrap EXPORT UnloadScrap EXPORT GetScrap EXPORT ZeroScrap EXPORT PutScrap ;-----------------------------------Scrap----------------------------------- ; ; FUNCTION InfoScrap: pScrapStuff; ; ;-----------------------------------Scrap----------------------------------- ; InfoScrap TST scrapState ; <15Jun85> BGE.S @0 ; <15Jun85> SUBQ #4,SP ; <15Jun85> _ZeroScrap ; <15Jun85> ADDQ #4,SP ; <15Jun85> @0 ; <15Jun85> MOVE.L (SP)+,A0 ; RTS LEA scrapVars,A1 MOVE.L A1,(SP) JMP (A0) ;-----------------------------------Scrap----------------------------------- ; ; StdEntry ; ; This routine sets up the standard frame and builds the ; io command block for an open. Then it tries to create and then open ; the file ; ; ; Exit: ; A0 - io command block ; A3 - io command block ; D0 - IO error ; D4 - scrapInfo ; D5 - scrapHandle ; D6 - scrapState ; CC's - set according to Open or create result (e.g. IO error) ; ;-----------------------------------Scrap----------------------------------- ; StdEntry MOVE.L (SP)+,A1 ; save local return address LINK A6,#stdLink ; set up frame MOVEM.L D2-D7/A2-A4,-(SP) ; save the regs MOVE.L A1,-(SP) ; push back on return StdSetup MOVEM.L scrapVars,D4/D5/D6/A2 ; preload scrapInfo,handle,state and name LEA scrapHandle,A4 ; convenient pointer LEA scrapIO(A6),A0 ; set up io command block MOVE.L A0,A3 ; Save I/O ptr in A3 MOVE.L A2,IOFileName(A0) ; set file name ptr ; CLR.W IODrvNum(A0) ; use default drive MOVE.W BootDrive,IODrvNum(A0) ; use boot drive <27 Apr> CLR.W IOFileType(A0) ; clear type, permissions CLR.L IOOwnBuf(A0) ; use system buffer TST D6 ; scrapState = disk BNE goHome0 ; skip open if not on disk CSS ; Now try to open; if that fails, create and open the file StdOpen _Open ; try an open first BEQ.S goHome ; and escape if good CMP #OpWrErr,D0 ; if open already, pretend it's ok BEQ.S goHome0 CMP #FNFErr,D0 ; create if not there BNE.S goHome _Create ; io cmd set up BNE.S goHome _GetFileInfo ; set the finder shit <27 Apr> MOVE.L #'CLIP',IOFlUsrWds(A0) ; set type <27 Apr> MOVE.L #'MACS',IOFlUsrWds+4(A0); set creator <27 Apr> BSET #4,IOFlUsrWds+8(A0) ; set system bit <27 Apr> _SetFileInfo ; <27 Apr> BNE.S goHome ; <27 Apr> BRA StdOpen ; go open now ;-----------------------------------Scrap----------------------------------- ; ; FUNCTION UnloadScrap: LONGINT; ; FUNCTION LoadScrap: LONGINT; ; ; Reminder: ; D4 = scrapLength from StdEntry ; D5 = scrapHandle ; D6 = scrapState ; A4^ scrapHandle ; ;-----------------------------------Scrap----------------------------------- ; UnloadScrap BSR StdEntry ; go set up local world BNE.S err0Exit ; escape if IO error TST D6 ; escape if on disk or un-inited BLE.S ok0Exit ; scrapState is <=0' BSR StdOpen ; go open it BNE.S err0Exit ; more robust error checking MOVE.L D4,D0 ; pass the length BSR.S SetupRW ; set up IO cmd _Write ; write it out BNE.S err0Exit MOVE.L D5,A0 ; get rid of the handle _DisposHandle ; ignore errors from dispose CLR.L (A4)+ ; indicate on disk(scrapHandle) CLR.L (A4)+ ; indicate on disk(scrapState) ok0Exit MOVEQ #0,D0 ; no error err0Exit EXT.L D0 MOVE.L D0,8(A6) ; return result code (link+0 param) MOVEQ #0,D0 ; see you later StdExit MOVEM.L (SP)+,D2-D7/A2-A4 ; see you later StdUNLK UNLK A6 stdRTS MOVE.L (SP)+,A0 ADD D0,SP JMP (A0) goHome0 MOVEQ #0,D0 ; jam no error goHome RTS ; return LoadScrap BSR StdEntry ; go set up the world BNE err0Exit TST D6 ; check scrap state BNE.S ok0Exit ; escape if in memory MOVE.L D4,D0 ; get scrap length _NewHandle BNE err0Exit MOVE.L A0,(A4)+ ; save the new handle(scrapHandle) ADDQ.L #1,(A4)+ ; 0 disk => 1 memory(scrapState) MOVE.L D4,D0 ; pass the length BSR.S SetupRW2 ; set up IO cmd for read _Read ; read it in BRA err0Exit ;-----------------------------------Scrap----------------------------------- ; ; Set up Read/Write for IO in command block off A6 ; Absolute pos mode at beginning ; ; Entry: ; A0 - (SetupRW2) handle for scrap ; A3 - io cmd block ; D0 - length to read or write ; D5 - scrap handle from stdEntry ; Exit: ; A0 - iocommand block ; ;-----------------------------------Scrap----------------------------------- SetupRW MOVE.L D5,A0 ; set up buffer address SetupRW2 MOVE.L (A0),IOBuffer(A3) ; to scrap handle MOVE.L D0,IOByteCount(A3) ; set up the buffer count MOVE #1,IOPosMode(A3) ; absolute position CLR.L IOPosOffset(A3) ; position to beginning MOVE.L A3,A0 ; set up for I/O RTS ; return for read or write ;-----------------------------------Scrap----------------------------------- ; ; FUNCTION ZeroScrap: LONGINT; ; ; Reminder: ; D4 = scrapLength from StdEntry ; D5 = scrapHandle ; D6 = scrapState ; ;-----------------------------------Scrap----------------------------------- ; ZeroScrap BSR StdEntry ; D0 set to 0 by above BNE err0Exit MOVEQ.L #0,D0 MOVE.L D0,scrapInfo ; zero out info ADDQ #8,4(A4) ; increment validation word BPL.S @0 NEG 4(A4) ; force it positive @0 TST D6 BEQ.S zeroDisk ; see if on disk BGT.S zeroMem ; see if in memory ; allocate a new scrap _NewHandle ; create the handle if the location BRA.S zsExit ; was negative zeroMem MOVE.L D5,A0 ; get the existing handle _SetHandleSize zsExit MOVE.L A0,(A4)+ ; save the handle(scrapHandle) MOVE (A4)+,(A4) ; positive => 1 memory(scrapState) BRA err0Exit zeroDisk CLR.L IOLEOF(A0) ; set to zero _SetEOF BRA err0Exit ;-----------------------------------Scrap----------------------------------- ; ; FUNCTION GetScrap( hDest: Handle; what: ResType; ; VAR offset: LONGINT ): LONGINT; ; ; Reminder: ; D4 = scrapLength from StdEntry ; D5 = scrapHandle ; D6 = scrapState ; ;-----------------------------------Scrap----------------------------------- ; gsOffset EQU 8 ; VAR offset gsType EQU gsOffset+4 ; type gsHDest EQU gsType+4 ; destination handle gsResult EQU gsHDest+4 ; result longint ; Utility to "get" bytes from the scrap on disk or in memeory ; ; Entry: ; D3 contains byte offset in scrap ; D0 contains amount to read ; A1 points to receiving buffer DoGet TST D6 ; see if on disk BEQ.S getDisk ; Get the data from the scrap handle in memeory MOVE.L D5,A2 ; get scrapHandle MOVE.L (A2),A0 ; set up source for move ADD.L D3,A0 ; add in offset _BlockMove BRA goHome0 ; return zero ; Get the data from the file getDisk BSR SetupRW ; fill out I/O command blk MOVE.L D3,IOPosOffset(A0) ; absolute read @ D3 MOVE.L A1,IOBuffer(A0) ; point to dest buffer _Read RTS GetScrap BSR StdEntry ; Open things up BNE.S err12Exit ; escape if file error ; Make sure scrap exists <27 Apr> ; MOVEQ #noScrapErr,D0 ; scrap don't exist <27 Apr> ; TST D6 ; check scrap state <27 Apr> ; BMI.S err12Exit ; escape if not zeroed <27 Apr> BSR FailSafe ; <27 Apr> BMI.S err12Exit ; escape if error on init <27 Apr> ; Start looking through the scrap ; ; D0 is used for read lengths ; D3 is the offset @ which to read ; A1 points to dest buffer MOVEQ #0,D3 ; start offset readNext CMP.L D4,D3 ; see if done with list BGE.S notFound MOVEQ #scrData,D0 ; read object header(8) LEA dType(A6),A1 ; buffer to place stuff BSR.S DoGet ; go get it BNE.S err12Exit okRead ADDQ.L #scrData,D3 ; point to next entry(more below) MOVE.L (A1)+,D7 ; get type from file CMP.L gsType(A6),D7 ; and compare BEQ.S foundIt ADD.L (A1)+,D3 ; by adding 8 plus length ADDQ.L #1,D3 ; round up to even BCLR D0,D3 ; assume D0 = 0 BRA readNext ; We now have D3 offset to correct entry, A1 points to object's length ; If the dest handle is nil, escape after passing back offset and D0 ; contains the length notFound MOVEQ #noTypeErr,D0 ; couldn't find type BRA.S err12LExit ; We now have D3 offset to correct entry, A1 points to object's length ; If the dest handle is nil, escape after passing back offset and D0 ; contains the length foundIt MOVE.L gsOffset(A6),A2 ; set up var address MOVE.L D3,(A2) ; return the offset to caller MOVE.L (A1),D0 ; Get object's length for return in D0 MOVE.L D0,20(A6) ; and return length for now MOVE.L gsHDest(A6),D1 ; set up destination BEQ.S go12Exit ; return length if no handle MOVE.L D1,A0 ; set it's size _SetHandleSize BNE.S err12Exit ; Do the actual fetch of the object MOVE.L (A1),D0 ; and read it's length MOVE.L (A0),A1 ; and read into dereferenced handle BSR.S DoGet BEQ.S go12Exit ; if no error(the length is stuffed above) err12Exit ; see you later EXT.L D0 ; turn OS error into a long err12LExit MOVE.L D0,20(A6) ; and return error code go12Exit ; link(8) + params(12) MOVEQ #12,D0 BRA StdExit ; Called by get and put to insure the scrap is zeroed if it hasn't been inited ; yet. This removes the noScrapErr error and makes it easier to use. ; Entered with cc's set according to D6 (just after StdEntry) FailSafe ; <27 Apr> TST D6 ; what's the scoop? <27 Apr> BGE.S @0 ; is scrap inited? <27 Apr> SUBQ #4,SP ; zero the scrap <27 Apr> _ZeroScrap MOVE.L (SP)+,D0 BMI.S err12Exit ; pass error along if there <27 Apr> BSR StdSetup @0 RTS ;-----------------------------------Scrap----------------------------------- ; ; FUNCTION PutScrap( length: LONGINT; what: ResType; pSource: Ptr ): LONGINT; ; ; Doesn't check for existing object of that type because caller ; will have always zeroed scrap prior to this. ; ; Reminder: ; D4 = scrapLength from StdEntry ; D5 = scrapHandle ; D6 = scrapState ; ;-----------------------------------Scrap----------------------------------- ; psSource EQU 8 ; source ptr psType EQU psSource+4 ; type psLength EQU psType+4 ; source length psResult EQU psLength+4 ; result PutScrap BSR StdEntry ; set up frame ; MOVEQ #noScrapErr,D0 ; assume the worst <27 Apr> ; TST D6 ; see if on disk <27 Apr> ; BMI.S err12Exit ; <27 Apr> BSR FailSafe ; make sure it's inited <27 Apr> BMI.S err12Exit ; escape if error on init <27 Apr> BSR SetupRW ; get ready for a write <27 Apr, moved> MOVEQ #scrData,D0 ; length of type LEA psType(A6),A0 ; save type and length BSR.S DoPut ; ignore error, catch it below MOVE.L psSource(A6),A0 ; get source ptr MOVE.L psLength(A6),D0 ; save the length's worth MOVEQ #-2,D1 ; evenizer mask ADDQ.L #1,D0 ; round up to even AND.L D1,D0 BSR.S DoPut ; errors handled on exit MOVE.L D4,-(A4) ; and save new scrap length(ScrapInfo) BRA.S err12Exit ; see you later D0 set from above DoPut MOVE.L D4,IOPosOffset(A3) ; set file position to old size ADD.L D0,D4 ; add this length to get new size TST D6 ; see if on disk BEQ.S putDisk MOVE.L D5,A1 ; get scrap handle _PtrAndHand ; add on to handle ;roll in FixPutScrap from ScrapMgrPatches.a. If _PtrAndHand returns an error ; (in D0), then we skip over the updating of low-mem scrapSize and cut back the ; scrap handle to the last scrapSize bz.s @exitDoPut ; Exit if PtrAndHand succeeded move.w d0,-(sp) ; save orginal error code move.l scrapSize,d0 ; resize handle to last good size, to delete last 'next entry' _SetHandleSize ; move.w (sp)+,d0 ; return with original error code addq #4,sp ; Pop DoPut callerÕs return address bra err12Exit ; And exit immediately. @exitDoPut rts ; putDisk MOVE.L A0,IOBuffer(A3) ; stuff ptr to data MOVE.L D0,IOByteCount(A3) ; 8 bytes of I/O MOVE.L A3,A0 ; restore I/O command _Write ;roll in F2PutScrap from ScrapMgrPatches.a. If _Write returns an error ; (in D0), then we skip over the updating of low-mem scrapSize and cut back the ; scrap handle to the last scrapSize bz.s @exitPutDisk ; Exit if Write succeeded move.w d0,-(sp) ; save orginal error code move.l scrapSize,ioMix(a0) ; resize file to last good size, to delete last 'next entry' _SetEOF ; move.w (sp)+,d0 ; return with original error code addq #4,sp ; Pop PutDisk callerÕs return address bra err12Exit ; And exit immediately @exitPutDisk RTS ; END