;CU.ASM - A PROGRAM TO MOVE FILES FROM ONE USER NUMBER TO ANOTHER ; ;From an idea by Aubrey Hutchinson of Pompano Beach, Florida ;published in Dr. Dobbs Journal, March 1983. ;See: Dr. Dobbs Clinic, page 82. ; ;Written by: Robert Wilcox ; 920 N. Washington ; Owosso, MI 48867 ;Date written: 2 Mar 83 ;Rewritten : 25 Mar 85 to show filenames as they are changed ; and allow for operator to be asked before each ; file operation. This revision was patterned ; after ERAQ, from which much of the code was ; borrowed. ; ;OPERATION: CU [dr:]FILENAME.TYPE s,d [/N] ; ;Where dr: is the drive designator (optional), ; s is the source user number, and ; d is the destination user number. ; N means don't ask before changing each file. ; ;Filename.typ may be ambiguous, same as with the ERA command. ; ;EXAMPLE: ; ; CU *.ASM 0,3 /N ; ;This would transfer all files of type ASM from user 0 to user 3 ;without asking before each transfer. ; ;CAUTION: If a file already exists in the destination user area, ;it is possible with this program to create another one with ;the same name. Using the ERA command will then erase BOTH ;of them. ; ;NOTE: R/O files will be R/W after the transfer. ; ORG 00100H BOOT EQU 00000H BDOS EQU 00005H FCB1 EQU 0005CH FCB2 EQU 0006CH ; ;CP/M FUNCTIONS CONIN EQU 1 ; Console in function TYPE EQU 2 ; Console type function PRINTF EQU 9 ; Print string function STATUS EQU 11 ; Console status function SEARCHF EQU 17 ; Search for first file SEARCHN EQU 18 ; Search for next file DELETE EQU 19 ; Delete file function CDISK EQU 25 ; Get current disk function SETDMA EQU 26 ; Set DMA function SETFIL EQU 30 ; Set file attributes USERF EQU 32 ; Get/set user ; ;ASCII EQUATES CTRLC EQU 3 ; Control - C TAB EQU 9 CR EQU 13 LF EQU 10 ESC EQU 27 ; MAXUSR: EQU 15 ; Highest valid user - can be ; made higher to 'hide' files GETRDY: LHLD 1 ; get warm start entry point LXI D,-654H DAD D ; subtract 654h bytes SHLD LOCE5 ; save address for later MOV A,M ; check to see that byte there CPI 0E5H ; is E5... JZ GETSET ; jump if it's there, LHLD 6 ; otherwise search thru FIND: INX H ; memory looking for MOV A,H ; the code for MVI M,E5, MVI C,xx CPI 0 ; If H=0 we have JZ BAD ; gone too far... MOV A,M ; Otherwise keep searching CPI 36H JNZ FIND INX H SHLD LOCE5 MOV A,M CPI 0E5H JNZ FIND INX H MOV A,M CPI 0EH JZ GETSET JMP FIND ; BAD: LXI D,BADMSG ; print bad system msg CALL PRTSTR ; and return to CCP.... RET ; GETSET: CALL INIT ; Initialize terminal if needed LDA FCB1 + 1 CPI ' ' JNZ NOWGO ; Jump if something was typed CALL CLRSCR ; No file spec'd, print help msg CALL REVON ; Reverse video on LXI D,PGMNAM ; Show the program name CALL PRTSTR CALL REVOFF ; Reverse video off LXI D,HLPMSG ; Help message CALL PRTSTR RET ; and return to CCP NOWGO: LXI H,FCB2 + 1 ; get source user # CALL GETNUM JC GOMORE ; if ok... BADNUM: LXI D,BADNR ; otherwise print bad number msg CALL PRTSTR ; and return to CCP.... JMP ABORT GOMORE: STA SOURCE ; save source user # MVI E,0FFH ; get current user # MVI C,USERF CALL BDOS STA USRNR ; save it LHLD POINTR CALL GETNUM ; get dest user # JNC BADNUM ; quit if bad number... STA DEST MOV E,A ; otherwise dest to E LDA SOURCE ; get source # and quit if same as CMP E ; dest. JZ BADNUM ; otherwise... PUSH D PUSH PSW LDA FCB1 ; get drive designation ANA A JNZ GOTDRV MVI C,CDISK ; get default drive CALL BDOS ; returns 0 for drive A INR A ; fix it so 1 = drive A... GOTDRV: ADI '@' ; convert drive to ASCII STA DRIVE ; save it LHLD LOCE5 ; point to location of e5h in CCP. POP PSW POP D MOV M,E ; replace the e5h with the dest user # MOV E,A ; source to E for bdos call MVI C,USERF CALL BDOS ; set user to source LXI H,80H MOV B,M MOV A,B ORA A JZ NOTN ; Skip if no characters typed FINDN: INX H ; else check each character MOV A,M CPI '/' ; Was it "/"? JNZ FINDLP DCR B JZ NOTN INX H MOV A,M CPI 'N' JZ YESN FINDLP: DCR B JNZ FINDN JMP NOTN YESN: STA NAFLG NOTN: LXI D,DBUF MVI C,SETDMA CALL BDOS LXI D,FCB1 MVI C,SEARCHF CALL BDOS INR A JZ FINIS GETFN: DCR A ; Adjust Accum to point to filename ADD A ; A=A*32 ADD A ADD A ADD A ADD A LXI H,DBUF ; Point to disk buffer MVI D,0 ; Add in MOV E,A ; offset for DAD D ; this file XCHG ; DE points to filename in disk buffer LHLD BUFPTR ; Point HL to buffer MVI C,32 ; # of bytes to move MOVE: LDAX D ; Move filename from disk buffer MOV M,A ; to main buffer INX H INX D DCR C JNZ MOVE SHLD BUFPTR ; Update pointer LDA FCOUNT ; Add 1 to file count INR A STA FCOUNT LXI D,FCB1 MVI C,SEARCHN ; Look for more files CALL BDOS INR A JNZ GETFN ; Get next filename LXI H,BUFFER ; All found, point to first one SHLD BUFPTR ; ;SEE IF WE TYPED "/N", IF YES, SAY "^C TO QUIT" LDA NAFLG CPI 'N' JNZ PRTFN CALL REVON LXI D,CTCMSG ; "^C" message CALL PRTSTR CALL REVOFF ; ;PRINT THE FILE SPECS PRTFN: CALL CRLF MVI A,8 ; Print 8 char's PUSH H INX H CALL TYPEB PUSH H MVI E,'.' ; Print a period CALL PCHAR POP H MVI A,3 ; Print file type CALL TYPEB MVI E,' ' ; And a couple of spaces CALL PCHAR MVI E,' ' CALL PCHAR POP H LXI D,9 ; Look at DAD D ; the MOV A,M ; R/O bit RLC JNC NOTRO ; Skip if not R/O, else MVI A,0FFH ; set R/O flag STA ROFLG NOTRO: INX H ; Look at SYS bit MOV A,M RLC JNC NOTSYS ; Skip if not SYS, else MVI A,0FFH ; set SYS flag STA SYSFLG NOTSYS: LDA ROFLG MOV B,A LDA SYSFLG ORA B JNZ PRTATR ; Jump if file is R/O or SYS MVI E,TAB CALL PCHAR JMP ASK ; PRTATR: MVI E,'(' ; Print file attributes CALL PCHAR LDA ROFLG ORA A JZ SKPRO ; Skip if not R/O LXI D,ROMSG ; "R/O" CALL PRTSTR SKPRO: LDA ROFLG ORA A JZ SKPCOM ; Don't print ',' if we didn't print 'R/O' MVI E,',' CALL PCHAR SKPCOM: LDA SYSFLG ORA A JZ SKPSYS LXI D,SYSMSG ; "SYS" CALL PRTSTR SKPSYS: MVI E,')' CALL PCHAR ASK: LDA NAFLG ; See if must ask to change file CPI 'N' ; 'N' = no ask JZ DOIT LXI D,QUERY ; Move this file? CALL PRTSTR MVI C,CONIN ;Get response CALL BDOS ANI 05FH ; Make upper case CPI CTRLC ; ^C to quit JZ FINIS CPI 'Y' ; Y to move the file DOIT: CZ CHANGE MVI C,STATUS ; Check if anything typed CALL BDOS ; on keyboard ANA A JZ CONT ; Continue if not, else MVI C,CONIN ; get character CALL BDOS CPI CTRLC ; Abort if it was ^C JZ FINIS CONT: MVI A,0 ; Reset the flags STA ROFLG STA SYSFLG LDA FCOUNT ; Count down DCR A STA FCOUNT JZ FINIS ; If no more LHLD BUFPTR LXI D,32 ; Move to next filename DAD D SHLD BUFPTR JMP PRTFN ; CHANGE: LHLD BUFPTR ; Get drive and LDA FCB1 ; move it to buffer MOV M,A LDA ROFLG ORA A ; If not R/O JZ RWOK ; skip next, else PUSH H ; Make it LXI D,9 ; R/W DAD D MOV A,M ANI 07FH MOV M,A POP H PUSH H XCHG MVI C,SETFIL CALL BDOS POP H ;CHANGE THE FILE'S USER BY CALLING THE DELETE FUNCTION RWOK: XCHG MVI C,DELETE CALL BDOS LDA CHGCNT INR A STA CHGCNT RET ; ;PRINT THE NUMBER OF FILES CHANGED AND RETURN TO CCP ;(IF ABORTING, PRINT ABORT MSG FIRST) FINIS: CPI CTRLC CZ ABORT CALL CRLF CALL REVON LDA CHGCNT MVI H,0 MOV L,A CALL PT3DEC ; Print H/L as 3 decimal digits LXI D,REPORT ; "FILES CHANGED " CALL PRTSTR LDA CHGCNT ; See if any files were changed ANA A ; Skip if none changed, otherwise JZ FINIS1 ; show 'from here to there' message LXI D,FRMMSG ; "FROM " CALL PRTSTR LDA DRIVE MOV E,A CALL PCHAR XRA A STA PRTFLG LDA SOURCE MVI H,0 MOV L,A CALL PT2DEC LXI D,TOMSG ; " TO " CALL PRTSTR LDA DRIVE MOV E,A CALL PCHAR XRA A STA PRTFLG LDA DEST MVI H,0 MOV L,A CALL PT2DEC FINIS1: CALL REVOFF CALL CRLF ;RESTORE ORIGINAL USER AND REPLACE E5 ERASE CODE LHLD LOCE5 MVI M,0E5H LDA USRNR MOV E,A MVI C,USERF CALL BDOS JMP BOOT ; ABORT: CALL REVON LXI D,ABTMSG ; "PROGRAM ABORTED" CALL PRTSTR CALL REVOFF RET ; ;Print H/L as 3 decimal digits PT3DEC: LXI B,-100 ; Divide by 100 CALL DIVIDE MOV A,E ; Result to A STA PRTFLG ; Save it for later CPI 0 ; Suppress leading 0 JZ PT2DEC CALL PRTDIG PT2DEC: LXI B,-10 ; Divide by 10 CALL DIVIDE LDA PRTFLG ; Have we printed a char? ANA A MOV A,E JNZ NOSKIP ; Yes, then don't skip a 0 CPI 0 JZ LSTDIG NOSKIP: CALL PRTDIG LSTDIG: MOV A,L CALL PRTDIG RET ; ;GETNUM - GETS NUMBER FROM COMMAND LINE - QUITS AT NON-NUMERIC CHAR. ; RETURNS WITH CARRY SET IF VALID USER. ; GETNUM: MVI E,0 ; clear accumulation GNLOOP: MOV A,M ; get the character INX H ; point to next one CALL CKNR ; conv. to binary & check if valid # JC GNDONE ; if not 0-9 MOV D,A ; save present digit of user # MOV A,E ; get prev. accumulation CALL X10 ; and mult it X 10 ADD D ; add in present digit MOV E,A ; save the accumulated value JMP GNLOOP ; get next char. GNDONE: MOV A,E ; retrieve the digit CPI MAXUSR + 1 ; see if valid - ret w/carry set if ok SHLD POINTR ; remember where we left off RET ; ;CKNR - Returns carry set if A is not a valid ASCII digit between 0 and 9 ; otherwise returns carry=0 and A converted to the binary value of ; the digit ; CKNR: SUI '0' RC CPI 10 CMC RET ; X10: ADD A ; A=A*2 MOV B,A ADD A ; A=A*4 ADD A ; A=A*8 ADD B ; A=A*10 RET ; ;TYPE NUMBER OF CHAR'S IN B REG TYPEB: PUSH PSW PUSH H MOV E,M CALL PCHAR POP H POP PSW INX H DCR A JNZ TYPEB RET ; ;Divide HL/BC, returns quotient in E, remainder in L DIVIDE: MVI E,0FFH DVLOOP: INR E DAD B JC DVLOOP MOV A,B CMA MOV B,A MOV A,C CMA MOV C,A INX B DAD B RET ; ;PRINT VALUE IN ACCUM AS ASCII DIGIT PRTDIG: ADI 030H PUSH H MOV E,A CALL PCHAR POP H RET ; ;SEND CARRIAGE RETURN, LINEFEED TO CRT CRLF: MVI E,CR CALL PCHAR MVI E,LF PCHAR: PUSH H MVI C,TYPE CALL BDOS POP H RET ; ;CLEAR CRT SCREEN CLRSCR: LXI D,CLSMSG JMP PRTSTR ; INIT: LXI D,NITSTR ; Initialize CRT for reverse video JMP PRTSTR ; REVOFF: LXI D,OFFSTR ; Reverse video off JMP PRTSTR ; REVON: LXI D,ONSTR ; Reverse video on ; PRTSTR: PUSH H PUSH D MVI C,PRINTF CALL BDOS POP D POP H RET ; ;VIDEO ATTRIBUTE STRINGS ; Strings for video attributes are stored here, each ending with '$' ; Initialized to null strings. Modify as desired for your terminal. CLSMSG: DB '$' ; Clear screen ONSTR: DB '$' ; Reverse video start tag OFFSTR: DB '$' ; Reverse video end tag NITSTR: DB '$' ; Initialize scrn for reverse video ; ;MESSAGES - CTCMSG: DB CR,LF,TAB,'Use ^C to quit',TAB,TAB,CR,LF,'$' QUERY: DB TAB,'Change this file? $' ABTMSG: DB CR,LF,LF,'Program aborted.$' REPORT: DB ' files changed $' FRMMSG: DB 'from $' TOMSG: DB ' to $' PGMNAM: DB TAB,'CU - a program to change files from one user to' DB ' another.',TAB,'$' HLPMSG: DB CR,LF,LF DB 'ENTER:',CR,LF,LF DB TAB,TAB,'CU [dr:]filename.typ s,d [/N]',CR,LF,LF DB TAB,'dr:=drive designator (optional)',CR,LF DB TAB,'s=source user number (where it is now)',CR,LF DB TAB,'d=destination user number (where you want the file)',CR,LF DB TAB,'N means don''t ask before changing each file.',CR,LF DB TAB,'filename.typ may be ambiguous, just as in the' DB ' ERA command.',CR,LF,LF DB 'EXAMPLE:',CR,LF,LF DB TAB,TAB,'CU *.ASM 0,3 /N',CR,LF,LF DB TAB,'This would transfer all files of type ASM from' DB ' user 0 to user 3',CR,LF DB TAB,'without asking before changing each file.' DB CR,LF,'$' BADMSG: DB CR,LF,'System does not match program',CR,LF,'$' BADNR: DB CR,LF,'Bad user number entered',CR,LF DB 'Enter CU for help.',CR,LF,'$' ; ROMSG: DB 'R/O$' SYSMSG: DB 'SYS$' ; ;STORAGE AREA ; PRTFLG: DB 0 ; Flag for decimal print routine DRIVE: DB 'A' ; Default drive SOURCE: DB 0 ; temp storage for source # DEST: DB 0 ; temp storage for destination # POINTR: DW 0 ; pointer to next character for dest user # LOCE5: DW 0 ; location of e5h in CCP USRNR: DB 0 ; Current user BUFPTR: DW BUFFER ; Buffer pointer storage FCOUNT: DB 0 ; File counter storage CHGCNT: DB 0 ; Changed file counter storage ROFLG: DB 0 ; Read-only flag (FF=R/O) SYSFLG: DB 0 ; SYSTEM flag (FF=SYS) NAFLG: DB 0 ; No ask flag (0=ask) DBUF: DS 128 ; Disk buffer BUFFER: DB 0 ; File name storage starts here ; END GETRDY