;*************************** AMUS Program Label ****************************** ; Filename: MOVE.M68 Date: 10/23/91 ; Category: UTIL Hash Code: 335-634-657-433 Version: 1.2 ; Initials: ULTR/US Name: DAVID PALLMANN ; Company: ULTRASOFT CORPORATION Telephone #: 5163484848 ; Related Files: ; Min. Op. Sys.: AMOSL 1.3B/AMOS32 1.0 Expertise Level: INT ; Special: after assembly: .LNKLIT MOVE,WLDSCN (needs WLDSCN.OBJ) ; Description: Moves files between PPNs (on same disk) super-fast ;***************************************************************************** ;MOVE.M68 ;Moves a file to a different user file directory ;A freeware product from UltraSoft; use the program but don't sell it ; ;Written by: David Pallmann ; ;Edit History: ;1.0 19-Feb-86 created. /DFP ;1.0A 23-Feb-86 correct handling of invalid account numbers (thanks Brad). /DFP ;1.1 08-Sep-86 added wildcarding support. /DFP ;1.2 23-Oct-91 - add ersatz name support (example: .MOVE *.* TO TEMP:). /DFP ; - add warnings about traditional format disks only. ; ;MOVE transfers the file descriptor information from one User File Directory ;to another, effectively changing the file's "home" from one PPN to another. ;Restrictions: (1) the source and destination account must be on the same ;disk; (2) the source and destination accounts must both exist (MOVE does ;not create PPNs); (3) both source and destination PPNs must be in the user's ;project (for security reasons) unless logged into [1,2], in which case ;anything goes. ; ;In addition to this source file, you will also need WLDSCN.OBJ, available on ;the AMUS network. ; ;How to assemble this program: (1) .M68 MOVE ; (2) .LNKLIT MOVE,WLDSCN ; ;This program is for traditional format disks only. It should not be used ;on extended format disks. VMAJOR =1 VMINOR =2 EXTERN $FNPPN EXTERN W.INIT,W.SPEC,W.NEXT ;[1.1] SEARCH SYS SEARCH SYSSYM ;the following is a definition of a Master File Directory. Every disk or ;other block-structured device has one or more Master File Directory blocks. ;The first MFD is always block 1. Near the end of the MFD is a link word. ;If this word is non-zero, it contains the link to the next MFD block. The ;2nd MFD block may in turn also link to another block. Finally, the link ;word will be zero at some point, indicating the end of the MFD chain. ; ;A Master File Directory block contains 63 PPN entries, each of which ;describe a PPN on the disk. The information contained in a PPN entry is ;(1) the account number (PPN); (2) the block number of the corresponding ;UFD (see next set of comments), and (3) the LOG password for the account. ;These elements are symbolically defined here: .OFINI .OFDEF MF.PPN,2 ; project-programmer number .OFDEF MF.LNK,2 ; link to 1st UFD block .OFDEF MF.PSW,4 ; password packed RAD50 .OFSIZ MF.SIZ ; (size of a PPN entry) ;The directory for an account is maintained in one or more User File ;Directory (UFD) blocks. Like MFD blocks, UFDs contain link words. Unlike ;the MFD, the UFD blocks contain their link word at the very beginning of ;the block. After the link word there are 42 file entries. File entries ;contain the following information: (1) filename; (2) file extension; (3) ;size of the file (in blocks); (4) number of bytes in the last block of the ;file (if this is a negative number then the file is contiguous); (5) the ;first block number of the file. The following symbols define a file entry: .OFINI .OFDEF UF.FIL,4 ; filename packed RAD50 .OFDEF UF.EXT,2 ; file extension packed RAD50 .OFDEF UF.SIZ,2 ; file size in blocks .OFDEF UF.ACT,2 ; active count .OFDEF UF.LNK,2 ; link to first block of file .OFSIZ UF.ESZ ;The following symbols define local memory used by MOVE .OFINI .OFDEF DISK,D.DDB ; DDB used for disk I/O .OFDEF OLDPPN,2 ; old PPN of file .OFDEF NEWPPN,2 ; new PPN of file .OFDEF OLDUFD,2 ; block number of old PPN UFD .OFDEF NEWUFD,2 ; block number of new PPN UFD .OFDEF FILSIZ,2 ; file size in blocks .OFDEF FILACT,2 ; active count .OFDEF FILLNK,2 ; link to 1st block of file .OFDEF COUNT,2 ; number of files MOVEd [1.1] .OFDEF CVTBUF,4 ; RAD50 conversion buffer [1.2] .OFSIZ MEMSIZ ; (size of this memory area) ;the following symbols define registers by name for ease of code reading MEM=A5 ; A5 points to local memory ;the following macro moves a WORD value to a LONGWORD destination DEFINE MOVWL SRC,DST CLR D7 MOVW SRC,D7 MOV D7,DST ENDM ;The following macros define entry points to the wildcard scanner ; WINIT initializes the scanner and returns A4 pointing to work memory ; WSPEC processes a command line wildcard specification ; WSCAN returns the a matching file specification ; ;The normal calling sequence goes something like this: ; ; WINIT ; initialized - must preserve A4 from now on ; ........ ; LEA A2,addr ; point A2 to file specification ; WSPEC XYZ ; process spec - use .XYZ as default extension ; ........ ; LOOP: WSCAN ; get next file spec, return in DDB @A4 ; BNE exit ; branch if no more files ; ........ ; JMP LOOP ; DEFINE WINIT IF NDF,W.INIT,EXTERN W.INIT CALL W.INIT ENDM DEFINE WSPEC EXT IF NDF,W.SPEC,EXTERN W.SPEC CALL W.SPEC IF B,EXT,ASCII /???/ IF NB,EXT,ASCII /'EXT/ BYTE 0 ENDM DEFINE WSCAN IF NDF,W.SCAN,EXTERN W.SCAN CALL W.SCAN ENDM ;Start of code ;Program HeaDeR indicates attributes of this program and stores version number ;GET IMPure area allocates local memory and points MEM (A5) to it. ;Lastly, call the WINIT routine, which initializes the wildcard scanner, and ;points A4 to the base of a work area. A4 must be preserved throughout the ;code in order for other wildcard calls (W.SPEC and W.SCAN) to work. [1.1] START: PHDR -1,PV$RPD!PV$WPD,PH$REE!PH$REU GETIMP MEMSIZ,MEM WINIT ;[1.1] INIT DISK(MEM) ; allocate an I/O buffer for DDB ;Command line processing ;The proper command line syntax is: .MOVE {file} TO [a,b] ;(the "TO" is optional, and so are the left and right brackets) ;If the user has entered a blank command line, or an invalid one, we set ;him (or her) straight by branching to HELP: below. CMDLIN: BYP ; bypass any spaces/tabs LIN ; end of command line? BEQ HELP ; yes - empty line - give help ;[1.1] FSPEC DISK(MEM),DAT ; load DDB with file specification WSPEC ; process wildcard file spec [1.1] JNE EXIT ; exit on bad spec [1.1] BYP ; bypass more white space CMMB @A2,#'T ; is the "T" of "TO" present? BNE 10$ ; no INC A2 ; bypass the "T" CMMB (A2)+,#'O ; is the "O" of "TO" present? BNE HELP ; no - error - give help BYP ; bypass more white space 10$: CMMB @A2,#'[ ; is left bracket present? BNE 20$ ; no - branch INC A2 ; bypass bracket 20$: NUM ; is a PPN present? JEQ GETPPN ; yes - branch [1.1] ALF ; ersatz name? [1.2] JEQ GETERZ ; yes - branch [1.2] HELP: TYPECR The MOVE command transfers a file to a different account. TYPECR CRLF ; TYPECR ;[1.1] TYPECR < .MOVE *.* TO MYACCT:> ;[1.2] CRLF ; TYPECR Note: this command is for traditional format disks only. CRLF ; EXIT ;Command line format seems reasonably valid. Get the new PPN from the command ;line and store it in NEWPPN(MEM). Store the old PPN in OLDPPN(MEM). If ;the user specified a PPN in the {file} parameter of the command line, the ;old PPN will be at DISK+D.PPN(MEM). If the user did not specify a source ;PPN, we default to where he/she is logged into, and we get the PPN by ;pointing to the user's Job Control Block and copying JOBUSR(JCB). GETPPN: GTPPN ; get new PPN JNE BADPPN ; [1.0A] MOVW D1,NEWPPN(MEM) ; store it in memory MOVW DISK+D.PPN(MEM),OLDPPN(MEM) ; store old PPN BNE 10$ ; branch if one was there JOBIDX A0 ; otherwise point to JCB MOVW JOBUSR(A0),OLDPPN(MEM) ; and get old PPN from there 10$: BR GETNXT ; [1.2] GETERZ: LEA A1,CVTBUF(MEM) ; convert ersatz spec [1.2] PACK ; from ASCII to [1.2] PACK ; two RAD50 words [1.2] CMPB (A2)+,#': ; colon present (expected)? [1.2] JNE BADPPN ; no error [1.2] MOV CVTBUF(MEM),D0 ; get ersatz name RAD50 [1.2] MOV ERSATZ,A0 ; index ersatz table [1.2] 10$: TST @A0 ; end of ersatz table? [1.2] JEQ BADPPN ; yes - error [1.2] CMP D0,EZ.NAM(A0) ; is this a match? [1.2] BEQ 20$ ; yes - get PPN [1.2] ADD #EZ.SIZ,A0 ; advance to next entry [1.2] BR 10$ ; loop [1.2] 20$: MOVW EZ.PPN(A0),NEWPPN(MEM) ; store new PPN [1.2] JEQ BADPPN ; no PPN - error [1.2] ;get next file from wildcard scanner and call the MOVE routine, which does ;the actual work of moving the file from one PPN to another [1.1] ;if no more files, we are done [1.1] GETNXT: CTRLC EXIT ; branch on ^C [1.1] WSCAN ; get next file [1.1] JNE EXIT ; no more files [1.1] CALL MOVE ; move the file [1.1] BR GETNXT ; on to next file [1.1] EXIT: MOVWL COUNT(MEM),D1 ; [1.1] BEQ 10$ ; [1.1] DCVT 0,OT$TRM ; [1.1] BR 20$ ; [1.1] 10$: TYPE %No ; [1.1] 20$: TYPE < file> ; [1.1] CMPW D1,#1 ; [1.1] BEQ 30$ ; [1.1] TYPE s ; [1.1] 30$: TYPECR < moved> ; [1.1] EXIT ; [1.1] ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;MOVE - move file in DDB @A4 to account NEWPPN(MEM) on same disk ;copy file specification to work DDB DISK(MEM) MOVE: MOVW D.DEV(A4),DISK+D.DEV(MEM) ;[1.1] MOVW D.DRV(A4),DISK+D.DRV(MEM) ;[1.1] MOV D.FIL(A4),DISK+D.FIL(MEM) ;[1.1] MOVW D.EXT(A4),DISK+D.EXT(MEM) ;[1.1] MOVW D.PPN(A4),DISK+D.PPN(MEM) ;[1.1] MOVW D.PPN(A4),OLDPPN(MEM) ;[1.1] ;See if the file actually exists ;If not, we cannot proceed CHKOFL: LOOKUP DISK(MEM) ; does file exist? BEQ CHKNFL ; yes CALL CANNOT TYPECR file not found ;[1.1] EXIT RTN ;[1.1] ;See if the file exists in the new PPN; if so, we can't copy this file there CHKNFL: MOVW NEWPPN(MEM),DISK+D.PPN(MEM) LOOKUP DISK(MEM) BNE 10$ CALL CANNOT TYPE PRPPN NEWPPN(MEM) TYPECR ] ;[1.1] EXIT RTN ;[1.1] 10$: MOVW OLDPPN(MEM),DISK+D.PPN(MEM) ;The next step is to see if the old and new PPNs are really out there on ;the device specified. Fortunately, there is a subroutine provided with ;AMOS/L, called $FNPPN, which figures out which MFD block a particular PPN ;is defined in. If $FNPPN fails (which will happen if the user specified ;a non-existent account number), an appropriate (scary) error message will ;be output and the user will be exited to the all-too-familiar dot. CHKOLD: MOVW OLDPPN(MEM),D1 ; load the old PPN into D1 LEA A2,DISK(MEM) ; point A2 to the DDB CALL $FNPPN ; look-up the PPN BNE BADPPN ; branch on error MOVW MF.LNK(A1),OLDUFD(MEM) ; store block number of new UFD CHKNEW: MOVW NEWPPN(MEM),D1 ; load the new PPN into D1 CALL $FNPPN ; look-up the PPN BNE BADPPN ; branch on error MOVW MF.LNK(A1),NEWUFD(MEM) ; store block number of new UFD BR CHKPRV ; and branch BADPPN: TYPECR ?Account Specification Error RTN ;make sure user isn't violating his privileges. A [1,2] user can copy to ;and from any account; any other user has to stay within his project. CHKPRV: JOBIDX A0 ; index Job Control Block CMMW JOBUSR(A0),#402 ; logged into [1,2]? BEQ LOKDIR ; yes - anything goes CMMB OLDPPN+1(MEM),JOBUSR+1(A0) ; is old PPN within project? BNE PRTERR ; no, branch CMMB NEWPPN+1(MEM),JOBUSR+1(A0) ; is new PPN within project? BEQ SCNOLD ; yes, branch PRTERR: CALL CANNOT TYPECR Protection Violation ;[1.1] EXIT RTN ;[1.1] LOKDIR: DSKDRL DISK(MEM) ; lock the device's directory ;Scan through the UFD blocks of the old PPN, looking for the file descriptor ;information for the {file} specified by the user. SCNOLD: MOVWL OLDUFD(MEM),DISK+D.REC(MEM) ; set block number to old PPN UFD BEQ UFDERR ; branch if end of UFD (should never happen) READ DISK(MEM) ; read UFD block MOV DISK+D.BUF(MEM),A0 ; index buffer MOVW (A0)+,OLDUFD(MEM) ; store next block link in memory MOV #42.,D0 ; get ready to scan 42 file entries FILOLD: CMM UF.FIL(A0),DISK+D.FIL(MEM) ; right file? BNE NXTOLD ; no CMMW UF.EXT(A0),DISK+D.EXT(MEM) ; right extension? BEQ FOUND ; yes NXTOLD: ADD #UF.ESZ,A0 ; advance to next file entry SOB D0,FILOLD ; loop till entire UFD scanned BR SCNOLD ; scan next UFD block UFDERR: TYPECR ?File not in UFD - can't continue ;[1.1] EXIT RTN ;[1.1] ;found file descriptor information (yea!) ;store size, link, and active count FOUND: MOVW UF.SIZ(A0),FILSIZ(MEM) MOVW UF.ACT(A0),FILACT(MEM) MOVW UF.LNK(A0),FILLNK(MEM) ;delete the file descriptor in the old UFD DELETE: MOVW #-1,UF.FIL(A0) WRITE DISK(MEM) ;Scan through the UFD blocks of the new PPN, looking for a delete file entry ;or the end of the directory. SCNNEW: MOVWL NEWUFD(MEM),DISK+D.REC(MEM) ; set block number to new PPN UFD BEQ UFDEND ; branch if end of UFD READ DISK(MEM) ; read UFD block MOV DISK+D.BUF(MEM),A0 ; index buffer MOVW (A0)+,NEWUFD(MEM) ; store next block link in memory MOV #42.,D0 ; get ready to scan 42 file entries FILNEW: CMMW UF.FIL(A0),#-1 ; delete file entry? BEQ ADDFIL ; yes TSTW UF.FIL(A0) ; end of UFD? BEQ INSFIL ; yes NXTNEW: ADD #UF.ESZ,A0 ; advance to next file entry SOB D0,FILNEW ; loop till entire UFD scanned BR SCNNEW ; scan next UFD block ;end of UFD reached, which means there aren't any free (unused or deleted) ;file entries. Rather than go through the hassle of allocating another ;directory block; updating the bitmap; and so forth, we take the easy way ;out by creating and immediately erasing a file in the new PPN area. This ;has the net effect of forcing AMOS/L to create another UFD block for us. UFDEND: DSKDRU DISK(MEM) ; unlock directories MOVW NEWPPN(MEM),DISK+D.PPN(MEM) OPENO DISK(MEM) CLOSE DISK(MEM) DSKDEL DISK(MEM) MOVW OLDPPN(MEM),DISK+D.PPN(MEM) MOVW NEWPPN(MEM),D1 ; load the new PPN into D1 CALL $FNPPN ; look-up the PPN MOVW MF.LNK(A1),NEWUFD(MEM) ; store block number of new UFD DSKDRL DISK(MEM) ; re-lock directories BR SCNNEW ; and start the scan over again ;insert a file entry into the UFD INSFIL: CMPW D0,#1 ; is this the last entry in UFD? BEQ ADDFIL ; yes CLRW UF.ESZ(A0) ; mark following entry as the last ;A0 now points to an available file entry in the UFD; write file name, ;extension, size, active count, and link into it. ADDFIL: MOV DISK+D.FIL(MEM),UF.FIL(A0) ; set filename MOVW DISK+D.EXT(MEM),UF.EXT(A0) ; set extension MOVW FILSIZ(MEM),UF.SIZ(A0) ; set size MOVW FILACT(MEM),UF.ACT(A0) ; set active count MOVW FILLNK(MEM),UF.LNK(A0) ; set link to first block WRITE DISK(MEM) ; rewrite UFD block DSKDRU DISK(MEM) ; unlock disk's directory ;Tell user what we did DONE: PFILE DISK(MEM) ;[1.1] TYPE < transferred to [> TYPE < to [> ;[1.1] PRPPN NEWPPN(MEM) TYPECR ] INCW COUNT(MEM) ;[1.1] ;[1.1] EXIT RTN ;[1.1] ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;Subroutine ;Print start of error message CANNOT: TYPESP ?Cannot move PFILE DISK(MEM) TYPE < - > RTN END .