home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 September
/
Simtel20_Sept92.cdr
/
msdos
/
pcmag
/
vol6n06.arc
/
COPYSAFE.ASM
next >
Wrap
Assembly Source File
|
1987-12-13
|
30KB
|
609 lines
; COPYSAFE.ASM
; Resident program that warns if you are
; about to copy over an existing file.
CODE SEGMENT ;***********************;
ASSUME CS:CODE,DS:CODE ; ;
ORG 100H ; REMEMBER TO EXE2BIN ;
; ;
;***********************;
START: JMP INITIALIZE
COPYRIGHT DB 'Copyright 1986 Ziff-Davis Publishing Co.',1AH
PROGRAMMER DB 'Michael J. Mefford'
OLD_21 DD ?
DOS_SEG DW ?
KBD_BUFFER DW ?
OLD_DTA DD ?
PARA1_START DW ?
PARA1_LENGTH DW ?
PARA2_START DW OFFSET PARA2
FIRST_TIME DB 0
GLOBAL_FLAG DB 0
MATCH_FLAG DB 0
PATH_FLAG DB 0
STAR_DOT_STAR DB '*.*'
COPY DB 'COPY'
WARNING_STRING DB 13,10,13,10,'WARNING: The following file(s)',13,10
DB 'exist and will be copied over.',13,10,13,10
DB 'Directory $'
QUERY_STRING DB 13,10,'Do you wish to continue (Y/N)? N$'
BLANK_LINE DB 13,10,'$'
;-----------------------------------------------------------------------;
; Upon entry to the INT 21 inteceptor, check if buffered keyboard input ;
; (function call 0Ah). If so, check the DOS segment as signature that ;
; we are at the DOS prompt. Check if command is COPY. If so, process. ;
;-----------------------------------------------------------------------;
NEW_21: PUSHF ;Save flags.
CMP AH,0AH ;Is it buffered keyboard
JZ CK_FIRST ; function call (0Ah)?
JMP NO_BUSINESS ;If no, exit.
CK_FIRST: CMP BYTE PTR CS:FIRST_TIME,0 ;Else, is it first time
JNZ CK_PROMPT ;through? If no, is it DOS?
MOV CS:FIRST_TIME,1 ;If yes, flag so won't repeat.
MOV CS:DOS_SEG,DS ;Save the segment as signature.
MOV CS:KBD_BUFFER,DX ;Save the offset as DOS buffer.
CK_PROMPT: PUSH AX ;Need to use AX to examine DS
MOV AX,DS ; for lack of segment compare
CMP AX,WORD PTR CS:DOS_SEG ; instruction. Is it DOS?
POP AX ;
JNZ NO_BUSINESS ;If no, exit.
CALL CS:OLD_21 ;Let user enter in the command.
PUSHF ;Upon return, save all registers.
PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSH DS
PUSH ES
PUSH SI
PUSH DI
PUSH BP
MOV AH,2FH ;Get the current disk
INT 21H ;transfer address
MOV WORD PTR CS:OLD_DTA,BX ;and save.
MOV WORD PTR CS:OLD_DTA[2],ES
MOV CS:MATCH_FLAG,0 ;Reset the match flag.
MOV SI,DX ;DX points to DOS kbd buffer.
INC SI ;Second byte has bytes read.
XOR CH,CH
MOV CL,[SI] ;Retrieve bytes read.
CMP CX,6 ;Has to be at least 6 bytes
JB QUICK_EXIT ; to be copy. E.g. (COPY A)
INC CX ;Increment count to include CR.
MOV BP,CX ;Use BP to keep track of bytes.
PUSH CS ;ES to point to us.
POP ES
INC SI ;Point to first byte of command.
MOV DI,OFFSET COMMAND ;Point to our work space.
CLD ;Move in forward direction.
REP MOVSB ;Move command to our work space.
PUSH CS
POP DS ;DS to point to us.
CALL CAPITALIZE ;Capitalize command.
MOV BX,OFFSET COMMAND
CALL PARA_START ;Find start of command.
JC EXIT
MOV CX,4
MOV SI,OFFSET COPY
MOV DI,BX
REPZ CMPSB ;Is it COPY command?
JNZ EXIT ;If no, exit.
MOV BX,DI ;If yes, adjust pointers.
SUB BP,4
CALL PARA_START ;Find start of first parameter.
JC EXIT ;If none, exit.
MOV PARA1_START,BX ;Else, save starting position.
CALL PARA_END ;Find end of parameter.
MOV CX,BX
SUB CX,PARA1_START ;Subtract start from end to
MOV PARA1_LENGTH,CX ; get length and save.
JMP SHORT CK_PARA2 ;Jump around exit.
;-----------------------------------------------------------------;
; These are the exits. One exit jumps directly to the old INT 21 ;
; if the call is not 0Ah. The other is the exit after we process ;
; the command. The exits are in the middle of the main routine ;
; so we can reach it with as many short jumps as possible. ;
;-----------------------------------------------------------------;
NO_BUSINESS: POPF
JMP CS:OLD_21 ;Jump to the old DOS routine.
EXIT: CMP MATCH_FLAG,1 ;Did we get a match?
JNZ DONE ;If no, we are done
CALL QUERY ;else, query if want to continue.
DONE: MOV DX,WORD PTR OLD_DTA ;Restore the data transfer
MOV AX,WORD PTR OLD_DTA[2] ; address.
MOV DS,AX
CALL SET_DTA
QUICK_EXIT: POP BP ;Restore all registers.
POP DI
POP SI
POP ES
POP DS
POP DX
POP CX
POP BX
POP AX
POPF
IRET ;Return.
;-------------------------------------------------------------------;
; Before we can check if the files exist the parameters must be ;
; parsed as one or two parameters and if drive and/or path only. ;
;-------------------------------------------------------------------;
CK_PARA2: CALL PARA_START ;Is there a second parameter?
JC ONE_PARA ;If no, process as one.
MOV SI,BX
CALL PARA_END ;Find end of parameter.
MOV CX,BX
SUB CX,SI ;Find length of parameter.
JMP SHORT STORE_PARA2 ;And put in work space.
ONE_PARA: MOV SI,PARA1_START
MOV DX,PARA1_LENGTH
ADD SI,DX ;Separate the path by
CALL SCAN_OFF ;scanning off "\" and ":".
CMP CX,PARA1_LENGTH ;Is there a path?
JNZ STORE_PARA2 ;If yes, store.
MOV BYTE PTR PARA2,0 ;Else, store zero.
JMP SHORT GET_PATHS ;Check if it is a path request.
STORE_PARA2: MOV DI,PARA2_START ;Store the second parameter.
REP MOVSB
MOV BYTE PTR [DI],0 ;Add the zero.
GET_PATHS: MOV DX,OFFSET DESTINATION ;Point to work space.
CALL SET_DTA
MOV DI,PARA1_START ;Is the first parameter a path?
CALL CK_PATH
JC CK_TARGET ;If no, check the second.
MOV SI,OFFSET STAR_DOT_STAR ;Else, add "*.*"
MOV CX,3
REP MOVSB
MOV BYTE PTR [DI],0 ;And the zero.
ADD WORD PTR PARA1_LENGTH,4 ;Adjust the length by "\*.*"
CK_TARGET: MOV DI,PARA2_START ;Is second a path request?
CALL CK_PATH
JNC APPEND ;If path, append the filename.
CALL CK_GLOBAL
CMP GLOBAL_FLAG,1
JZ CK_EXIST
CALL MATCH ;Else, it is a renamed file.
JMP EXIT ;Display and exit.
APPEND: MOV SI,PARA1_START
MOV DX,PARA1_LENGTH
ADD SI,DX ;Else, scan off path from source
CALL SCAN_OFF ; and add to target.
REP MOVSB
MOV BYTE PTR [DI],0 ;End with a byte of zero.
;----------------------------------------------------------------------;
; We are ready to see if the target file exists. This is done by ;
; checking if all matching files of source match the requested target. ;
;----------------------------------------------------------------------;
CK_EXIST: CALL CK_GLOBAL ;Check if global characters.
MOV DX,OFFSET SOURCE ;Point to work space.
CALL SET_DTA
MOV DX,PARA1_START
MOV CX,6
CALL FIND_FIRST ;Find first matching.
JNC GET_DEST
JMP EXIT ;If none, exit.
GET_DEST: MOV DX,OFFSET DESTINATION ;Point to target work space.
CALL SET_DTA
MOV DX,PARA2_START
MOV CX,6
CALL FIND_FIRST ;Find first matching.
JNC FIRST_COMP
JMP EXIT ;If none, exit.
FIRST_COMP: CALL COMPARE ;See if filenames identical.
NEXT_DEST: CALL FIND_NEXT ;Find next matching target.
JC NEXT_SOURCE ;If none, get next source.
CALL COMPARE ;See if filenames identical.
JMP SHORT NEXT_DEST
NEXT_SOURCE: MOV DX,OFFSET SOURCE ;Switch DTA back to source.
CALL SET_DTA
CALL FIND_NEXT ;Get next matching source.
JNC GET_DEST ;If exist, get next target.
JMP EXIT ;Else, we are done here.
;*************;
; SUBROUTINES ;
;*************;
;---------------------------------------------------;
; This subroutine will capitalize the command line. ;
;---------------------------------------------------;
CAPITALIZE: MOV SI,OFFSET COMMAND ;Point to command line.
MOV CX,BP ;Get length of command.
CK_CAP: CMP BYTE PTR [SI],'a'
JB NEXT_CAP ;If the byte is between
CMP BYTE PTR [SI],'z' ;"a" and "z", capitalize
JA NEXT_CAP ;by stripping the 5th bit.
AND BYTE PTR [SI],5FH
NEXT_CAP: INC SI
LOOP CK_CAP
RET
;--------------------------------------------------;
; These subroutines are INT 21 DOS function calls. ;
;--------------------------------------------------;
SET_DTA: MOV AH,1AH ;Set disk transfer address.
INT 21H
RET
FIND_FIRST: MOV AH,4EH ;Find first matching file.
INT 21H
RET
FIND_NEXT: MOV AH,4FH ;Find next matching file.
INT 21H
RET
PRINT_STRING: MOV AH,9 ;Print string.
INT 21H
RET
PRINT_CHAR: MOV DL,AL
MOV AH,2 ;Display output.
INT 21H
RET
GET_DIR: MOV DL,0
MOV AH,47H ;Get current directory.
INT 21H
RET
CHANGE_DIR: MOV AH,3BH ;Change current directory.
INT 21H
RET
;-----------------------------------------------------;
; This subroutine will find the start of a parameter. ;
;-----------------------------------------------------;
PARA_START: CMP BYTE PTR [BX],32 ;Is it space character or below?
JBE NEXT_START ;If yes, get next character.
CMP BYTE PTR [BX],'/' ;Is it the switch character?
JNZ END_START ;If no, we have the start.
INC BX ;Else, adjust pointers.
DEC BP
JZ END_COM ;If end of command, no parameter.
NEXT_START: INC BX ;Adjust pointers to next char.
DEC BP
JNZ PARA_START
END_COM: STC ;Indicate found parameter
RET ; by setting carry flag.
END_START: CLC ;Indicate did not find parameter
RET ; by clearing carry flag.
;---------------------------------------------------;
; This subroutine will find the end of a parameter. ;
;---------------------------------------------------;
PARA_END: INC BX ;Adjust pointers.
DEC BP
CMP BYTE PTR [BX],32 ;Is it space character or below?
JBE END_PARA ;If yes, found end.
CMP BYTE PTR [BX],'/' ;Is it switch character?
JNZ PARA_END ;If no, get next character.
MOV BYTE PTR [BX],0 ;Else, replace with zero for
JMP SHORT PARA_END ; DOS and get next.
END_PARA: MOV BYTE PTR [BX],0 ;Mark end of parameter with zero.
RET
;----------------------------------------------------------------;
; This subroutine will check to see if parameter is a path only. ;
;----------------------------------------------------------------;
CK_PATH: MOV DX,DI ;Save the start in DX and SI.
MOV SI,DI
CMP BYTE PTR [DI],0 ;If zero need to add "*.*"
JZ PATH
FIND_END: INC DI ;Find the end of parameter.
CMP BYTE PTR [DI],0
JNZ FIND_END
CMP BYTE PTR [DI-1],':' ;Is it drive only?
JZ PATH ;If yes, path request.
CMP BYTE PTR [DI-1],'\' ;Is it root only?
JZ PATH ;If yes, path request.
CMP BYTE PTR [DI-1],'*' ;Is it "*.*"?
JZ CK_PATH_END ;If yes, not path request.
MOV CX,10H
CALL FIND_FIRST
JNC CK_DIR
POP AX ;If it doesn't exist, exit.
JMP EXIT
CK_DIR: CMP BYTE PTR DESTINATION+21,10H ;Is it directory?
JNZ CK_PATH_END ;If no, not path request.
CMP BYTE PTR [DI],'\' ;Do we need to add "\"?
JZ PATH
ADD_PATH: MOV BYTE PTR [DI],'\' ;If yes, do so.
INC DI ;And adjust pointer.
PATH: CLC ;Indicate found path.
RET
CK_PATH_END: STC ;Indicate path not found.
RET
;--------------------------------------------------------------------;
; This subroutine will scan off the path by looking for "\" and ":". ;
;--------------------------------------------------------------------;
SCAN_OFF: XOR CX,CX ;Zero in counter.
NEXT_SCAN: CMP BYTE PTR [SI-1],'\' ;Is it "\"?
JZ END_SCAN ;If yes, got path.
CMP BYTE PTR [SI-1],':' ;Is it ":"?
JZ END_SCAN ;If yes, got path.
DEC SI ;Else, adjust pointers.
INC CX
CMP CX,DX ;If at start of para, no path.
JB NEXT_SCAN
END_SCAN: RET
;-----------------------------------------------------------------;
; This subroutine will display a string until it comes to a zero. ;
;-----------------------------------------------------------------;
ASCIIZ_PRINT: LODSB ;Get a byte.
CMP AL,0 ;Is it a zero?
JZ PRINT_END ;If yes, we are done here.
CALL PRINT_CHAR ;Else, print it.
JMP SHORT ASCIIZ_PRINT ;And get next character.
PRINT_END: RET
;-------------------------------------------------------------;
; This subroutine will compare each matching source with each ;
; target to determine if the target exists. If there are any ;
; global characters, only those positions will be compared. ;
;-------------------------------------------------------------;
COMPARE: MOV SI,OFFSET SOURCE+30 ;Point to source and
MOV DI,OFFSET DESTINATION+30 ;target filenames.
CMP GLOBAL_FLAG,1 ;If global characters,
JZ GLOBAL ;do a global compare.
NEXT_COMP: CMPSB ;Are the bytes the same?
JNZ NO_MATCH ;If no, no match.
CMP BYTE PTR [DI-1],0 ;Are we at end of filename?
JNZ NEXT_COMP ;If no, get next byte.
JMP SHORT MATCH ;Else, we have a match.
GLOBAL: MOV BP,OFFSET DESTINATION+2 ;Point to DOS work space.
NEXT_HALF: XOR BX,BX ;Zero in counter.
NEXT_LENGTH: CMP BYTE PTR [DI+BX],'.' ;Get length of target by
JZ GOT_LENGTH ;looking for "." or zero.
CMP BYTE PTR [DI+BX],0
JZ GOT_LENGTH
INC BX
JMP SHORT NEXT_LENGTH
GOT_LENGTH: MOV CX,BX
NEXT_BYTE: CMP BYTE PTR [SI],'.' ;Did we get to end of source
JZ NO_MATCH ; before we got end target?
CMP BYTE PTR [SI],0 ;If yes, no match.
JZ NO_MATCH
CMP BYTE PTR DS:[BP],'?' ;Is it a global char request?
JZ COMP_BYTE ;If yes, compare.
INC DI ;Else, adjust pointers.
INC SI
JMP SHORT NEXT_WILD ;And get next byte.
COMP_BYTE: CMPSB ;Are the bytes the same?
JNZ NO_MATCH ;If no, no match.
NEXT_WILD: INC BP ;Adjust pointer.
LOOP NEXT_BYTE ;Get next byte.
CMP BYTE PTR [DI],0 ;Are we at end of target?
JNZ CK_EXTEN ;If no, get extension.
CMP BYTE PTR [SI],0 ;If yes, are we end of source?
JZ MATCH ;If yes, we have match.
CMP BYTE PTR DS:[BP],'?' ;Else, is global request?
JNZ MATCH ;If no, we have a match.
JMP SHORT NO_MATCH ;Else, no match.
CK_EXTEN: INC DI ;Bump pointer past "."
NEXT_EXTEN: CMP BYTE PTR [SI],0 ;Are we at end of source?
JZ NO_MATCH ;If yes, no match.
INC SI ;Else get extension.
CMP BYTE PTR [SI-1],'.' ;by looking for "."
JNZ NEXT_EXTEN
MOV BP,OFFSET DESTINATION+10 ;Adjust DOS work space.
JMP SHORT NEXT_HALF ;Get extension of filename.
MATCH: CMP MATCH_FLAG,1 ;Is this the first match?
JZ GO_PRINT ;If no, skip warning.
CALL PRINT_WARNING ;Else, print warning.
GO_PRINT: MOV AL,9 ;Print a tab to indent.
CALL PRINT_CHAR
MOV SI,OFFSET DESTINATION+30 ;Point to filename to print.
CALL ASCIIZ_PRINT
CR_LF: MOV DX,OFFSET BLANK_LINE ;Add carriage return.
CALL PRINT_STRING
NO_MATCH: RET
;-----------------------------------------------------------------;
; This subroutine will check for any global characters in target. ;
;-----------------------------------------------------------------;
CK_GLOBAL: MOV GLOBAL_FLAG,0 ;Reset global flag.
MOV SI,PARA2_START ;Point to target.
NEXT_GLOBAL: LODSB ;Get a byte.
CMP AL,'*' ;Is it "*"?
JZ FOUND_GLOBAL ;If yes, got global.
CMP AL,'?' ;Is it "?"?
JZ FOUND_GLOBAL ;If yes, got global.
CMP AL,0 ;Is it end of parameter?
JNZ NEXT_GLOBAL ;If no, get next byte.
RET
FOUND_GLOBAL: MOV GLOBAL_FLAG,1
RET
;---------------------------------------------------------------------;
; This subroutine will print the warning and directory of the target. ;
;---------------------------------------------------------------------;
PRINT_WARNING: MOV PATH_FLAG,0 ;Reset path flag.
MOV DX,OFFSET WARNING_STRING ;Print warning.
CALL PRINT_STRING
MOV MATCH_FLAG,1 ;Indicate match.
MOV AH,19H ;Get current drive.
INT 21H
PUSH AX ;And save.
MOV SI,OFFSET CURRENT_DIR+1 ;Get current directory.
CALL GET_DIR ;And save.
MOV BP,PARA2_START ;Does target include drive?
CMP BYTE PTR DS:[BP+1],':'
JNZ GET_END ;If no, check for path.
MOV DL,BYTE PTR DS:[BP] ;Else, get drive requested.
SUB DL,'A' ;Convert to DOS format.
MOV AH,0EH ;And change drive.
INT 21H
MOV SI,OFFSET SOURCE_DIR+1
CALL GET_DIR ;Get directory of target drive.
MOV PATH_FLAG,1 ;Indicate that we must restore.
GET_END: INC BP ;Get end of target parameter
CMP BYTE PTR DS:[BP],0 ; by looking for zero.
JNZ GET_END
MOV SI,BP
MOV DX,BP ;Get length of target parameter.
SUB DX,PARA2_START
CALL SCAN_OFF ;Is path requested?
CMP CX,DX
JZ GET_DISK ;If no, get default drive.
DEC DX
CMP CX,DX
JZ ROOT
CMP BYTE PTR [SI-1],'\' ;Is it directory request?
JNZ GET_DISK ;If no, get default.
CMP BYTE PTR [SI-2],':' ;Is it drive request?
JZ ROOT ;If yes, adjust pointer.
DEC SI
ROOT: MOV BH,[SI] ;Save byte and replace with
MOV BYTE PTR [SI],0 ;zero to please DOS and get
MOV DX,PARA2_START ;requested directory.
CALL CHANGE_DIR
MOV [SI],BH ;Replace character.
GET_DISK: MOV AH,19H ;Get current drive.
INT 21H
ADD AL,'A' ;Convert to ASCII.
CALL PRINT_CHAR ;And display.
MOV AL,':' ;Display ":\" as well.
CALL PRINT_CHAR
MOV AL,'\'
CALL PRINT_CHAR
MOV SI,OFFSET WORKING_DIR ;Get the target directory.
CALL GET_DIR
CMP BYTE PTR WORKING_DIR,0
JZ END_WARNING
CALL ASCIIZ_PRINT ;And display.
END_WARNING: MOV DX,OFFSET BLANK_LINE ;Double space
CALL PRINT_STRING ; by printing to carriage
CALL PRINT_STRING ; returns and linefeeds.
CMP PATH_FLAG,1 ;Did we change directory?
JNZ DRIVE_ONLY
MOV BYTE PTR SOURCE_DIR,'\'
MOV DX,OFFSET SOURCE_DIR
CALL CHANGE_DIR ;If yes, restore target directory
DRIVE_ONLY: POP DX
MOV AH,0EH ;Restore the drive as well.
INT 21H
MOV BYTE PTR CURRENT_DIR,'\' ;Now restore default drive's
MOV DX,OFFSET CURRENT_DIR ; directory.
CALL CHANGE_DIR
RET
;---------------------------------------------------------------------;
; This subroutine will ask if you wish to continue. If no, the first ;
; byte of the command line will be replaced with a carriage return. ;
;---------------------------------------------------------------------;
QUERY: MOV DX,OFFSET QUERY_STRING
CALL PRINT_STRING ;Print the string.
MOV AX,0E08H ;Back the cursor up one by
INT 10H ; printing BS (8) TTY.
INKEY: MOV AH,7 ;Get a keystroke.
INT 21H
CMP AL,27 ;Is it Esc?
JZ CANCEL ;If yes, cancel COPY
CMP AL,13 ;Is it carriage return?
JZ RESPONSE ;If yes, get response.
AND AL,5FH ;Capitalize keystroke.
CMP AL,'Y' ;Is it "Y"?
JZ PRINT_KEY ;If yes, print it.
CMP AL,'N' ;Is it "N"?
JZ PRINT_KEY ;If yes, print it.
MOV AX,0E07H ;Else, print bell character at
INT 10H ; current cursor position.
JMP SHORT INKEY ; and get next keystroke.
PRINT_KEY: XOR BH,BH ;Print the character at
MOV CX,1 ; the current cursor position.
MOV AH,0AH
INT 10H
JMP SHORT INKEY ;Get next keystroke.
RESPONSE: XOR BH,BH
MOV AH,8 ;Get character at current
INT 10H ; cursor position.
CMP AL,'Y' ;Is it "Y"?
JZ END_QUERY ;If yes, done here.
CANCEL: MOV BX,KBD_BUFFER ;Else, it must be "N" and
MOV AX,DOS_SEG ; cancel request by putting a
MOV ES,AX ; CR in first byte of DOS
MOV BYTE PTR ES:[BX+2],13 ; command line buffer.
END_QUERY: RET
;---------------------------------------------------;
; This routine installs this program in the path of ;
; interrupt 21h and then exits but stays resident. ;
;---------------------------------------------------;
INITIALIZE: MOV AX,3521h ;Get DOS 21 interrupt.
INT 21H
MOV WORD PTR OLD_21,BX ;Save old interrupt.
MOV WORD PTR OLD_21[2],ES
MOV DX,OFFSET NEW_21 ;Install new interrupt.
MOV AX,2521h
INT 21H
MOV DX,OFFSET END_RESIDENT ;Terminate but stay resident.
INT 27h
PARA2 EQU INITIALIZE ;This is COPYSAFE's reserved
COMMAND EQU PARA2+70 ; work space.
DESTINATION EQU COMMAND+140 ;It is placed at the end of
SOURCE EQU DESTINATION+64 ; code to keep the basic data
CURRENT_DIR EQU SOURCE+64 ; listing to a minimum.
WORKING_DIR EQU CURRENT_DIR+64
SOURCE_DIR EQU WORKING_DIR+64
END_RESIDENT EQU SOURCE_DIR+65
CODE ENDS
END START