You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1093 lines
28 KiB
1093 lines
28 KiB
page ,132
|
|
title COMMAND COPY routines.
|
|
;/*
|
|
; * Microsoft Confidential
|
|
; * Copyright (C) Microsoft Corporation 1991
|
|
; * All Rights Reserved.
|
|
; */
|
|
|
|
comment % -----------------------------------------------------------------
|
|
|
|
;*** COPY.ASM
|
|
|
|
Source files: copy.asm, copypr1.asm, copypr2.asm
|
|
|
|
|
|
;*** MODIFICATION HISTORY
|
|
|
|
11/01/83 EE Added a few lines at the end of SCANSRC2 to get multiple
|
|
file concatenations (eg copy a.*+b.*+c.*) to work properly.
|
|
11/02/83 EE Commented out the code in CPARSE which added drive designators
|
|
to tokens which begin with path characters so that PARSELINE
|
|
will work correctly.
|
|
11/04/83 EE Commented out the code in CPARSE that considered paren's to be
|
|
individual tokens. That distinction is no longer needed for
|
|
FOR loop processing.
|
|
11/17/83 EE CPARSE upper case conversion is now flag dependent. Flag is
|
|
1 when Cparse is called from COPY.
|
|
11/17/83 EE Took out the comment chars around code described in 11/04/83
|
|
mod. It now is conditional on flag like previous mod.
|
|
11/21/83 NP Added printf
|
|
12/09/83 EE CPARSE changed to use CPYFLAG to determine when a colon should
|
|
be added to a token.
|
|
05/30/84 MZ Initialize all copy variables. Fix confusion with destclosed
|
|
NOTE: DestHand is the destination handle. There are two
|
|
special values: -1 meaning destination was never opened and
|
|
0 which means that the destination has been openned and
|
|
closed.
|
|
06/01/84 MZ Above reasoning totally specious. Returned things to normal
|
|
06/06/86 EG Change to fix problem of source switches /a and /b getting
|
|
lost on large and multiple file (wildcard) copies.
|
|
06/09/86 EG Change to use xnametrans call to verify that source and
|
|
destination are not equal.
|
|
|
|
06/24/90 DO If the destination of a file concatenation is the same as
|
|
first source file AND we run out of disk space before
|
|
completing the concatenation, restore the first source
|
|
file as best we can. See SeekEnd and CopErr. Bug #859.
|
|
|
|
M031 SR 10/11/90 Bug #3069. Use deny write sharing mode to open files
|
|
instead of compatibility mode. This gives lesser sharing
|
|
violations when files are opened for read on a copy.
|
|
|
|
% -------------------------------------------------------------------------
|
|
|
|
.xlist
|
|
.xcref
|
|
|
|
include comsw.asm
|
|
include dossym.inc
|
|
include syscall.inc
|
|
include sf.inc
|
|
include comseg.asm
|
|
include comequ.asm
|
|
.list
|
|
.cref
|
|
|
|
|
|
DATARES segment public byte
|
|
extrn VerVal:word
|
|
DATARES ends
|
|
|
|
TRANDATA segment public byte
|
|
extrn BadCd_Ptr:word
|
|
extrn Copied_Ptr:word
|
|
extrn Extend_Buf_Ptr:word
|
|
extrn Extend_Buf_Sub:byte
|
|
extrn File_Name_Ptr:word
|
|
extrn InBDev_Ptr:word
|
|
extrn Msg_Disp_Class:byte
|
|
extrn Overwr_Ptr:word
|
|
TRANDATA ends
|
|
|
|
TRANSPACE segment public byte
|
|
extrn AllSwitch:word
|
|
extrn ArgC:byte
|
|
extrn Ascii:byte
|
|
extrn Binary:byte
|
|
extrn BytCnt:word
|
|
extrn CFlag:byte
|
|
extrn Comma:byte
|
|
extrn Concat:byte
|
|
extrn Copy_Num:word
|
|
extrn CpDate:word
|
|
extrn CpTime:word
|
|
extrn CpyFlag:byte
|
|
extrn CurDrv:byte
|
|
extrn DestBuf:byte
|
|
extrn DestClosed:byte
|
|
extrn DestFcb:byte
|
|
extrn DestFcb2:byte
|
|
extrn DestHand:word
|
|
extrn DestInfo:byte
|
|
extrn DestIsDir:byte
|
|
extrn DestSiz:byte
|
|
extrn DestSwitch:word
|
|
extrn DestTail:word
|
|
extrn DestVars:byte
|
|
extrn DirBuf:byte
|
|
extrn Expand_Star:byte
|
|
extrn FileCnt:word
|
|
extrn FirstDest:byte
|
|
extrn FrstSrch:byte
|
|
extrn Inexact:byte
|
|
extrn MelCopy:byte
|
|
extrn MelStart:word
|
|
extrn Msg_Flag:byte
|
|
extrn NoWrite:byte
|
|
extrn NxtAdd:word
|
|
extrn ObjCnt:byte
|
|
extrn OCtrlZ:byte
|
|
extrn OFilePtr_Hi:word
|
|
extrn OFilePtr_Lo:word
|
|
extrn One_Char_Val:byte
|
|
extrn Parse_Last:word
|
|
extrn Plus:byte
|
|
extrn Plus_Comma:byte
|
|
extrn RdEof:byte
|
|
extrn ResSeg:word
|
|
extrn ScanBuf:byte
|
|
extrn SDirBuf:byte
|
|
extrn SrcBuf:byte
|
|
extrn SrcHand:word
|
|
extrn SrcInfo:byte
|
|
extrn SrcIsDev:byte
|
|
extrn SrcPt:word
|
|
extrn SrcSiz:byte
|
|
extrn SrcTail:word
|
|
extrn SrcVars:byte
|
|
extrn SrcXName:byte
|
|
extrn StartEl:word
|
|
extrn String_Ptr_2:word
|
|
extrn TermRead:byte
|
|
extrn Tpa:word
|
|
extrn UserDir1:byte
|
|
extrn Written:word
|
|
TRANSPACE ends
|
|
|
|
|
|
|
|
|
|
;*** COPY CODE
|
|
|
|
TRANCODE segment public byte
|
|
|
|
extrn CError:near
|
|
extrn CopErr:near
|
|
extrn TCommand:near
|
|
|
|
public Copy
|
|
|
|
assume cs:TRANGROUP,ds:TRANGROUP,es:TRANGROUP,ss:NOTHING
|
|
|
|
break Copy
|
|
|
|
Copy:
|
|
assume ds:TRANGROUP,es:TRANGROUP
|
|
|
|
; Initialize internal variables.
|
|
|
|
xor ax,ax ; AX = 0
|
|
mov Copy_Num,ax ; # files copied (destinations) = 0
|
|
mov SrcPt,ax ; cmd line ptr for source scan = 0
|
|
mov SrcTail,ax ; ptr to last element of source pathname = 0
|
|
mov CFlag,al ; 'destination file created' = false
|
|
mov NxtAdd,ax ; ptr into TPA buffer = 0
|
|
mov DestSwitch,ax ; destination switches = none
|
|
mov StartEl,ax ; CParse ptr to last pathname element = 0
|
|
mov DestTail,ax ; ptr to last element of dest pathname = 0
|
|
mov DestClosed,al ; 'destination file closed' = false
|
|
mov DestSiz,al ; length of destination pathname = 0
|
|
mov SrcSiz,al ; length of source pathname = 0
|
|
mov DestInfo,al ; destination pathname flags = none
|
|
mov SrcInfo,al ; source pathname flags = none
|
|
mov Inexact,al ; 'inexact copy' = false
|
|
mov DestVars,al ; 'dest pathname is directory' = false
|
|
mov SrcVars,al ; 'source pathname is directory' = false
|
|
mov UserDir1,al ; saved working directory = null
|
|
mov NoWrite,al ; 'no write' (source = dest) = false
|
|
mov RdEof,al ; 'read end of file' = false
|
|
mov SrcHand,ax ; source handle = 0
|
|
mov CpDate,ax ; copy date = 0
|
|
mov CpTime,ax ; copy time = 0
|
|
mov SrcIsDev,al ; 'source is device' = false
|
|
mov OCtrlZ,al ; 'Ctrl+Z removed from original' = false
|
|
mov OFilePtr_Lo,ax
|
|
mov OFilePtr_Hi,ax ; original destination file ptr = null
|
|
mov TermRead,al ; 'terminate read' = false
|
|
mov Comma,al ; '"+,," found' = false
|
|
mov Plus_Comma,al ; '"+,," found last time' = false (?)
|
|
mov Msg_Flag,al ;AN022; 'non-utility msg issued' = false
|
|
mov AllSwitch,ax ; all switches = none
|
|
mov ArgC,al ; source/dest argument count = 0
|
|
mov Plus,al ; '"+" in command line' = false
|
|
mov Binary,al ; 'binary copy' = false
|
|
mov Ascii,al ; 'ascii copy' = false
|
|
mov FileCnt,ax ; # files copied (destinations) = 0
|
|
mov Written,ax ; 'destination written to' = false
|
|
mov Concat,al ; 'concatenating' = false
|
|
mov MelCopy,al ; 'Mel Hallerman copy' = false
|
|
mov MelStart,ax ; Mel Hallerman cmd line ptr = 0
|
|
|
|
; Initialize buffers with double-nulls.
|
|
|
|
mov word ptr ScanBuf,ax
|
|
mov word ptr DestBuf,ax
|
|
mov word ptr SrcBuf,ax
|
|
mov word ptr SDirBuf,ax
|
|
mov word ptr DirBuf,ax
|
|
mov word ptr DestFcb,ax
|
|
|
|
mov ObjCnt,al ; # CParse cmd-line objects found = 0
|
|
|
|
dec ax ; AX = 0FFFFh
|
|
mov DestHand,ax ; destination handle = 'never opened'
|
|
mov FrstSrch,al ; 'first search for source' = true
|
|
mov FirstDest,al ; 'first time for dest' = true
|
|
mov DestIsDir,al ; 'haven't analyzed destination'
|
|
|
|
mov si,81h ; SI = ptr to command line
|
|
mov bl,PLUS_CHR ; BL = special delimiter = "+"
|
|
inc Expand_Star ; CParse 'expand * to ?s' = true
|
|
mov CpyFlag,1 ; CParse 'called from COPY' = true
|
|
|
|
|
|
;* Scan the command line for destination information.
|
|
|
|
DestScan:
|
|
xor bp,bp ; BP = switch flag accumulator
|
|
mov di,offset TRANGROUP:ScanBuf ; ES:DI = ptr to pathname buf
|
|
mov Parse_Last,si ;AN018; save cmd line ptr
|
|
invoke CParse ; parse next object
|
|
pushf ; save CParse flags
|
|
inc ObjCnt ; count object
|
|
|
|
test bh,80h
|
|
jz @f ; no "+" delimiter
|
|
mov Plus,1 ; "+" delimiter occurred
|
|
@@:
|
|
test bh,1
|
|
jz TestP2 ; not a switch
|
|
|
|
; Found a switch.
|
|
|
|
test bp,SwitchV ;AN038; Verify requested?
|
|
jz Not_SlashV ;AN038; No - set the switch
|
|
test AllSwitch,SwitchV ;AN038; Verify already entered?
|
|
jz Not_SlashV ;AN038; No - set the switch
|
|
;AD018; or AllSwitch,FBadSwitch ;AN038; Set up bad switch
|
|
or bp,FBadSwitch ;AN018; Set up bad switch
|
|
|
|
Not_SlashV: ;AN038;
|
|
or DestSwitch,bp ; assume destination
|
|
or AllSwitch,bp ; keep tabs on all switches
|
|
|
|
test bp,not SwitchCopy ;AN018; Bad switch?
|
|
jz Not_Bad_Switch ;AN018; Switches are okay
|
|
popf ;AN018; fix up stack
|
|
mov ax,BadSwt_ptr ;AN018; get "Invalid switch" message number
|
|
invoke Setup_Parse_Error_Msg ;AN018; setup to print the message
|
|
jmp CError ;AC018; exit
|
|
|
|
Not_Bad_Switch: ;AN018; switch okay
|
|
popf ; restore CParse flags
|
|
jc CheckDone ; found CR
|
|
jmp short DestScan ; continue scanning for destination
|
|
|
|
TestP2:
|
|
popf ; restore CParse flags
|
|
jc CheckDone ; found CR
|
|
|
|
test bh,80h
|
|
jnz @f ; found a "+pathname" argument
|
|
inc ArgC ; count independent pathname args
|
|
@@:
|
|
push si ; save cmd line ptr
|
|
mov ax,StartEl ; AX = ptr to last path element
|
|
mov si,offset TRANGROUP:ScanBuf ; SI = ptr to path string
|
|
sub ax,si ; AX = offset of last element
|
|
mov di,offset TRANGROUP:DestBuf ; DI = ptr to destination buf
|
|
add ax,di ; AX = ptr to last element in
|
|
; destination path buffer
|
|
mov DestTail,ax ; save ptr to last element
|
|
mov DestSiz,cl ; save path string length
|
|
|
|
inc cx ; CX = mov length (incl null)
|
|
rep movsb ; DestBuf = possible destination path
|
|
mov DestInfo,bh ; save CParse info flags
|
|
mov DestSwitch,0 ; clear destination switches
|
|
pop si ; SI = ptr into cmd line again
|
|
jmp DestScan ;AC018; continue scanning for dest
|
|
|
|
CheckDone:
|
|
|
|
; We reached the CR. The destination scan is finished.
|
|
|
|
; Disallow "copy file1+" as file overwriting itself.
|
|
;
|
|
; (Note that "copy file1+file2+" will be accepted, and
|
|
; equivalent to "copy file1+file2".)
|
|
|
|
; Bugbug: it looks like "copy /x file1+" would slip
|
|
; through this check, since the switch would count
|
|
; as another object in ObjCnt.
|
|
|
|
cmp Plus,1 ; "+" with
|
|
jne cdCont
|
|
cmp ArgC,1 ; one arg,
|
|
jne cdCont
|
|
cmp ObjCnt,2 ; two objects..
|
|
jne cdCont
|
|
mov dx,offset TRANGROUP:OverWr_ptr
|
|
jmp CopErr ; is file overwrite.
|
|
|
|
cdCont: mov al,Plus ; AL = '"+" occurred'
|
|
mov Concat,al ; if "+" occurred, we're concatenating
|
|
shl al,1
|
|
shl al,1
|
|
mov Inexact,al ; therefore making an inexact copy
|
|
|
|
mov al,ArgC ; AL = # independent arguments
|
|
or al,al
|
|
jnz Try_Too_Many ; more than 0 args; check if too many
|
|
|
|
mov dx,offset TRANGROUP:Extend_Buf_Ptr ; DX = ptr to msg block
|
|
mov Extend_Buf_Ptr,LessArgs_Ptr ; set msg # "param missing"
|
|
jmp short CError_ParseJ ; take parse error exit
|
|
|
|
Try_Too_Many:
|
|
cmp al,2
|
|
jbe ACountOk ; <= 2 arguments - ok
|
|
|
|
mov dx,offset TRANGROUP:Extend_Buf_Ptr ; DX = ptr to msg block
|
|
mov Extend_Buf_Ptr,MoreArgs_Ptr ; set msg # "too many params"
|
|
CError_ParseJ:
|
|
mov Msg_Disp_Class,PARSE_MSG_CLASS ; parse error message
|
|
CError4J:
|
|
jmp CError ; command error exit
|
|
|
|
|
|
ACountOk:
|
|
mov bp,offset TRANGROUP:DestVars ; BP = base of dest variables
|
|
|
|
; Bugbug: use of BP without segment override implies SS.
|
|
; SS is implicitly assumed to point at TRANGROUP.
|
|
|
|
cmp al,1
|
|
jne Got2Args ; we have 2 arguments
|
|
|
|
; Only one independent pathname argument on command line.
|
|
; Set destination to d:*.*, where d: is current drive.
|
|
|
|
; Bugbug: but is this appropriate for "copy x:file1+x:file2"?
|
|
; The two files would be appended as d:file1, rather than x:file1.
|
|
|
|
mov al,CurDrv ; AL = current drive (0 = A)
|
|
add al,CAPITAL_A ; AL = current drive letter
|
|
mov ah,':' ; AX = "d:"
|
|
mov [bp].siz,2 ; pathname length = 2
|
|
|
|
mov di,offset TRANGROUP:DestBuf ; ES:DI = ptr to dest path buf
|
|
stosw ; store "d:"
|
|
|
|
mov DestSwitch,0 ; clear destination switches
|
|
mov [bp].info,2 ; mark destination 'wildcard present'
|
|
mov [bp].isdir,0 ; mark destination 'not a directory'
|
|
invoke SetStars ; add wildcards
|
|
|
|
Got2Args:
|
|
|
|
; If destination pathname is "d:", add full wildcard filename
|
|
|
|
cmp [bp].siz,2
|
|
jne NotShortDest ; not two chars, can't be "d:"
|
|
|
|
mov al,':'
|
|
cmp destbuf+1,al
|
|
jne NotShortDest ; it's just a 2-character filename
|
|
|
|
or [bp].info,2 ; mark destination 'wildcard present'
|
|
mov di,offset TRANGROUP:DestBuf+2
|
|
; ES:DI = ptr after "d:"
|
|
mov [bp].isdir,0 ; mark destination 'not a directory'
|
|
invoke SetStars ; add wildcards
|
|
|
|
NotShortDest:
|
|
|
|
; If destination pathname ends with "\", try to make
|
|
; sure it's "d:\".
|
|
|
|
mov di,[bp].ttail ; DI = ptr to last path element
|
|
cmp byte ptr [di],0
|
|
jne ChkSwtches ; not a null, so last char not "\"
|
|
|
|
mov dx,offset TRANGROUP:BadCD_Ptr ; DX = ptr to msg block
|
|
mov al,':'
|
|
cmp byte ptr [di-2],al
|
|
jne CError4J ; it's not "d:\", exit with error msg
|
|
mov [bp].isdir,2 ; destination 'is a directory'
|
|
or [bp].info,6 ; destination wildcarded and contains
|
|
; path character
|
|
invoke SetStars ; add wildcards
|
|
|
|
ChkSwtches:
|
|
|
|
;AD018; mov ax,[ALLSWITCH]
|
|
;AD018; test ax,NOT SwitchCopy
|
|
;AD018; jz NOT_BAD_SWITCH ;AN000; Switches are okay
|
|
;AD018; MOV DX,OFFSET TranGroup:Extend_Buf_ptr ;AC000; get extended message pointer
|
|
;AD018; mov Extend_Buf_ptr,BadSwt_ptr ;AN000; get "Invalid switch" message number
|
|
;AD018; jmp short CERROR_PARSEJ ;AC000; Switch specified which is not known
|
|
;AD018; NOT_BAD_SWITCH:
|
|
|
|
; We have enough information about the destination for now.
|
|
|
|
; Turn on verify if requested. Save the current verify flag.
|
|
|
|
mov ax,AllSwitch ; AX = all switch flags
|
|
test ax,SwitchV
|
|
jz NoVerif ; no /v, no verify
|
|
|
|
mov ah,GET_VERIFY_ON_WRITE ; AH = 'Get Verify Flag'
|
|
int 21h ; call DOS
|
|
|
|
push ds
|
|
mov ds,ResSeg
|
|
assume ds:RESGROUP
|
|
xor ah,ah
|
|
mov VerVal,ax ; save current verify flag
|
|
pop ds
|
|
assume ds:TRANGROUP
|
|
mov ax,(SET_VERIFY_ON_WRITE shl 8) or 1 ; AX = 'Set Verify Flag'
|
|
int 21h ; call DOS
|
|
|
|
NoVerif:
|
|
|
|
;* Scan for first source.
|
|
|
|
xor bp,bp ; BP = switch flags accumulator
|
|
mov si,81h ; SI = ptr into command line
|
|
mov bl,PLUS_CHR ; BL = special CParse delimiter = "+"
|
|
ScanFSrc:
|
|
mov di,offset TRANGROUP:ScanBuf ; DI = ptr to pathname buf
|
|
invoke CParse ; parse first source pathname
|
|
test bh,1 ; switch?
|
|
jnz ScanFSrc ; yes, try again
|
|
or DestSwitch,bp ; include copy-wide switches on dest
|
|
|
|
; Set ascii copying mode if concatenating, unless /b is specified.
|
|
|
|
test bp,SWITCHB
|
|
jnz NoSetCAsc ; /b - explicit binary copy
|
|
cmp Concat,0
|
|
je NoSetCAsc ; we're not concatenating
|
|
mov Ascii,SWITCHA ; set ascii copy
|
|
NoSetCAsc:
|
|
call Source_Set ; set source variables
|
|
call FrstSrc ; set up first source copy
|
|
jmp FirstEnt ; jump into the copy loop
|
|
|
|
|
|
|
|
|
|
public EndCopy
|
|
|
|
EndCopy:
|
|
|
|
;* End of the road. Close destination, display # files
|
|
; copied (meaning # destinations), and go back to main
|
|
; transient COMMAND code.
|
|
|
|
call CloseDest
|
|
EndCopy2:
|
|
mov dx,offset TRANGROUP:Copied_Ptr
|
|
mov si,FileCnt
|
|
mov Copy_Num,si
|
|
invoke Std_PrintF
|
|
jmp TCommand ; stack could be messed up
|
|
|
|
|
|
|
|
|
|
SrcNonexist:
|
|
|
|
;* Source doesn't exist. If concatenating, ignore and continue.
|
|
; Otherwise, say 'file not found' and quit.
|
|
|
|
cmp Concat,0
|
|
jne NextSrc ; concatenating - go on to next source
|
|
|
|
; Set up error message.
|
|
|
|
mov Msg_Disp_Class,EXT_MSG_CLASS ; extended error msg
|
|
mov dx,offset TRANGROUP:Extend_Buf_Ptr ; DX = ptr to msg block
|
|
mov Extend_Buf_Ptr,ERROR_FILE_NOT_FOUND ; 'file not found' msg#
|
|
mov String_Ptr_2,offset TRANGROUP:SrcBuf ; point at bad pathname
|
|
mov Extend_Buf_Sub,ONE_SUBST ; 1 substitution
|
|
|
|
jmp CopErr ; print msg and clean up
|
|
|
|
|
|
|
|
|
|
SourceProc:
|
|
|
|
;* Preparatory processing for each source file.
|
|
; Called at FrstSrc for first source file.
|
|
|
|
call Source_Set ; set source variables & ascii/binary
|
|
cmp Concat,0
|
|
jne LeaveCFlag ; concatenating - leave CFlag alone
|
|
FrstSrc:
|
|
xor ax,ax
|
|
mov CFlag,al ; 'destination not created'
|
|
mov NxtAdd,ax ; copy buffer ptr = 0
|
|
mov DestClosed,al ; 'destination not closed'
|
|
LeaveCFlag:
|
|
mov SrcPt,si ; save cmd-line ptr
|
|
mov di,offset TRANGROUP:UserDir1 ; DI = ptr to buf for user's
|
|
; current dir
|
|
mov bp,offset TRANGROUP:SrcVars ; BP = base of source variables
|
|
invoke BuildPath ; cd to source dir, figure
|
|
; out stuff about source
|
|
mov si,SrcTail ; SI = ptr to source filename
|
|
return
|
|
|
|
|
|
|
|
|
|
NextSrc:
|
|
|
|
;* Next source. Come here after handling each pathname.
|
|
; We're done unless there are additional source pathnames
|
|
; to be appended.
|
|
;
|
|
; Note that all files matching an ambiguous pathname
|
|
; are processed before coming here.
|
|
|
|
cmp Plus,0
|
|
jne MoreCp ; copying "+" sources - keep going
|
|
|
|
EndCopyJ2:
|
|
jmp EndCopy ; done copying
|
|
|
|
MoreCp:
|
|
xor bp,bp ; BP = switch flags accumulator
|
|
mov si,SrcPt ; SI = ptr to current pos'n in cmd line
|
|
mov bl,PLUS_CHR ; BL = special delimiter = "+"
|
|
|
|
ScanSrc:
|
|
mov di,offset TRANGROUP:ScanBuf ; DI = ptr to pathname buf
|
|
invoke CParse ; parse first source name
|
|
jc EndCopyJ2 ; CR found - we're done
|
|
|
|
test bh,80h
|
|
jz EndCopyJ2 ; no "+" delimiter - we're done
|
|
|
|
test bh,1
|
|
jnz ScanSrc ; switch found - keep looking
|
|
|
|
; ScanBuf contains the next source pathname.
|
|
|
|
call SourceProc ; prepare this source
|
|
cmp Comma,1 ;g was +,, found last time?
|
|
jne NoStamp ;g no - try for a file
|
|
mov Plus_Comma,1 ;g yes - set flag
|
|
jmp SrcNonexist ;g we know we won't find it
|
|
|
|
NoStamp: ;g
|
|
mov Plus_Comma,0 ;g reset +,, flag
|
|
|
|
FirstEnt:
|
|
;
|
|
;M047
|
|
; The only case we need to worry about is when the source is wildcarded and
|
|
;the destination is not. For this case, ConCat is not yet set to indicate
|
|
;concatenation. We check for this case.
|
|
;
|
|
;NB: This change has been backed out and replaced by M048. This is not the
|
|
;right place to do this check.
|
|
;
|
|
|
|
; This is where we enter the loop with the first source.
|
|
|
|
mov di,FCB ; DI = ptr to FCB
|
|
mov ax,PARSE_FILE_DESCRIPTOR shl 8 ; 'Parse Filename'
|
|
int 21h ; call DOS
|
|
|
|
cmp byte ptr [si],0 ; did we parse the whole thing?
|
|
jne SrchDone ; no, error, simulate 'not found'
|
|
|
|
mov ax,word ptr SrcBuf ; AX = possible "d:"
|
|
cmp ah,':'
|
|
je @f ; AX = definite "d:"
|
|
mov al,'@' ; AL = drive 'letter' for current drive
|
|
@@:
|
|
or al,20h ; AL = lowercase drive letter
|
|
sub al,60h ; AL = drive id (0=current,1=A,..)
|
|
mov ds:FCB,al ; put drive id in FCB
|
|
|
|
; FCB contains drive and filename to search.
|
|
|
|
mov ah,DIR_SEARCH_FIRST ; AH = 'Find First File'
|
|
call Search
|
|
|
|
SrchDone:
|
|
pushf ; save flags from Search
|
|
invoke RestUDir1 ; restore users current directory
|
|
popf ; restore flags from search
|
|
jz @f ; found the source - continue
|
|
jmp SrcNonexist ; didn't find the source
|
|
@@:
|
|
xor al,al
|
|
xchg al,FrstSrch
|
|
or al,al
|
|
jz NextAmbig
|
|
|
|
SetNMel:
|
|
mov cx,12
|
|
mov di,offset TRANGROUP:SDirBuf
|
|
mov si,offset TRANGROUP:DirBuf
|
|
rep movsb ; save very first source name
|
|
|
|
NextAmbig:
|
|
xor al,al
|
|
mov NoWrite,al ; turn off nowrite
|
|
mov di,SrcTail
|
|
mov si,offset TRANGROUP:DirBuf + 1
|
|
invoke Fcb_To_Ascz ; SrcBuf has complete name
|
|
|
|
MelDo:
|
|
cmp Concat,0
|
|
jne ShowCpNam ; concatenating - show name
|
|
test SrcInfo,2 ; wildcard - show name
|
|
jz DoRead
|
|
|
|
ShowCpNam:
|
|
mov dx,offset TRANGROUP:File_Name_Ptr
|
|
invoke Std_PrintF
|
|
invoke CrLf2
|
|
|
|
DoRead:
|
|
call DoCopy
|
|
cmp Concat,0
|
|
jne NoDClose ; concatenating - don't close dest
|
|
|
|
call CloseDest ; close current destination
|
|
jc NoDClose ; concatenating - dest not closed
|
|
|
|
mov CFlag,0 ; 'destination not created'
|
|
|
|
NoDClose:
|
|
cmp Concat,0
|
|
je NoFlush ; not concatenating - don't flush
|
|
|
|
; Concatenating - flush output between source files so LostErr
|
|
; stuff works correctly.
|
|
|
|
invoke FlshFil
|
|
test MelCopy,0FFh
|
|
jz @f
|
|
jmp short DoMelCopy
|
|
@@:
|
|
NoFlush:
|
|
call SearchNext ; try next match
|
|
jnz NextSrcJ ; not found - finished with
|
|
; this source spec
|
|
mov DestClosed,0 ; 'destination not closed'
|
|
jmp NextAmbig ; do next ambig match
|
|
|
|
|
|
|
|
|
|
DoMelCopy:
|
|
cmp MelCopy,0FFh
|
|
je ContMel
|
|
mov si,SrcPt
|
|
mov MelStart,si
|
|
mov MelCopy,0FFh
|
|
ContMel:
|
|
xor bp,bp
|
|
mov si,SrcPt
|
|
mov bl,PLUS_CHR
|
|
ScanSrc2:
|
|
mov di,offset TRANGROUP:ScanBuf
|
|
invoke CParse
|
|
test bh,80h
|
|
jz NextMel ; no "+" - go back to start
|
|
|
|
test bh,1
|
|
jnz ScanSrc2 ; switch - keep scanning
|
|
|
|
call SourceProc
|
|
invoke RestUDir1
|
|
mov di,offset TRANGROUP:DestFcb2
|
|
mov ax,PARSE_FILE_DESCRIPTOR shl 8
|
|
int 21h
|
|
mov bx,offset TRANGROUP:SDirBuf + 1
|
|
mov si,offset TRANGROUP:DestFcb2 + 1
|
|
mov di,SrcTail
|
|
|
|
invoke BuildName
|
|
|
|
cmp Concat,0
|
|
je MelDoJ ; not concatenating - continue
|
|
|
|
; Yes, turn off nowrite because this part of the code
|
|
; is only reached after the first file has been dealt with.
|
|
|
|
mov NoWrite,0
|
|
|
|
MelDoJ:
|
|
jmp MelDo
|
|
|
|
NextSrcJ:
|
|
jmp NextSrc
|
|
|
|
NextMel:
|
|
call CloseDest
|
|
xor ax,ax
|
|
mov CFlag,al
|
|
mov NxtAdd,ax
|
|
mov DestClosed,al
|
|
mov si,MelStart
|
|
mov SrcPt,si
|
|
call SearchNext
|
|
jz SetNMelJ
|
|
jmp EndCopy2
|
|
SetNMelJ:
|
|
jmp SetNMel
|
|
|
|
|
|
|
|
|
|
SearchNext:
|
|
mov ah,DIR_SEARCH_NEXT
|
|
test SrcInfo,2
|
|
jnz Search ; do search-next if ambig
|
|
or ah,ah ; reset zero flag
|
|
return
|
|
|
|
Search:
|
|
push ax
|
|
mov ah,SET_DMA
|
|
mov dx,offset TRANGROUP:DirBuf
|
|
int 21h ; put result of search in dirbuf
|
|
pop ax ; restore search first/next command
|
|
mov dx,FCB
|
|
int 21h ; Do the search
|
|
or al,al
|
|
return
|
|
|
|
|
|
|
|
|
|
DoCopy:
|
|
mov si,offset TRANGROUP:SrcBuf ;g do name translate of source
|
|
mov di,offset TRANGROUP:SrcXName ;g save for name comparison
|
|
mov ah,XNAMETRANS ;g
|
|
int 21h ;g
|
|
|
|
mov RdEof,0 ; no EOF yet
|
|
|
|
mov ax,EXTOPEN shl 8 ; open the file
|
|
;M046
|
|
; For reads, the sharing mode should be deny none so that any process can
|
|
;open this file again in any other sharing mode. This is mainly to allow
|
|
;multiple command.com's to access the same file without getting sharing
|
|
;violations
|
|
;
|
|
mov bx,DENY_NONE or READ_OPEN_MODE ; open mode for COPY ;M046
|
|
xor cx,cx ; no special files
|
|
mov dx,READ_OPEN_FLAG ; set up open flags
|
|
int 21h
|
|
|
|
jnc OpenOk
|
|
|
|
; Bogosity: IBM wants us to issue Access Denied in this case.
|
|
; They asked for it...
|
|
|
|
jmp short Error_On_Source ;AC022; clean up and exit
|
|
|
|
OpenOk:
|
|
mov bx,ax ; save handle
|
|
mov SrcHand,bx ; save handle
|
|
mov ax,(FILE_TIMES shl 8)
|
|
int 21h
|
|
jc Error_On_Source
|
|
mov CpDate,dx ; save date
|
|
mov CpTime,cx ; save time
|
|
jmp short No_Copy_Xa ; (xa copy code removed)
|
|
|
|
|
|
Error_On_Source: ;AN022; we have a BAD error
|
|
invoke Set_Ext_Error_Msg ;AN022; set up the error message
|
|
mov String_Ptr_2,offset TRANGROUP:SrcBuf ;AN022; get address of failed string
|
|
mov Extend_Buf_Sub,ONE_SUBST ;AN022; put number of subst in control block
|
|
invoke Std_EprintF ;AN022; print it
|
|
cmp SrcHand,0 ;AN022; did we open the file?
|
|
je No_Close_Src ;AN022; no - don't close
|
|
call CloseSrc ;AN022; clean up
|
|
No_Close_Src: ;AN022;
|
|
cmp CFlag,0 ;AN022; was destination created?
|
|
je EndCopyJ3 ;AN022; no - just cleanup and exit
|
|
jmp EndCopy ;AN022; clean up concatenation and exit
|
|
EndCopyJ3: ;AN022;
|
|
jmp EndCopy2 ;AN022;
|
|
|
|
No_Copy_Xa:
|
|
mov bx,SrcHand ;AN022; get handle back
|
|
mov ax,(IOCTL shl 8)
|
|
int 21h ; get device stuff
|
|
and dl,DEVID_ISDEV
|
|
mov SrcIsDev,dl ; set source info
|
|
jz CopyLp ; source not a device
|
|
cmp Binary,0
|
|
je CopyLp ; ascii device ok
|
|
mov dx,offset TRANGROUP:InBDev_Ptr ; cannot do binary input
|
|
jmp CopErr
|
|
|
|
|
|
CopyLp:
|
|
mov bx,SrcHand
|
|
mov cx,BytCnt
|
|
mov dx,NxtAdd
|
|
sub cx,dx ; compute available space
|
|
jnz GotRoom
|
|
invoke FlshFil
|
|
cmp TermRead,0
|
|
jne CloseSrc ; give up
|
|
mov cx,BytCnt
|
|
GotRoom:
|
|
push ds
|
|
mov ds,Tpa
|
|
assume ds:NOTHING
|
|
mov ah,READ
|
|
int 21h
|
|
pop ds
|
|
assume ds:TRANGROUP
|
|
jc Error_On_Source ;AC022; give up if error
|
|
mov cx,ax ; get count
|
|
jcxz CloseSrc ; no more to read
|
|
cmp SrcIsDev,0
|
|
jne NoTestA ; is a device, ascii mode
|
|
cmp Ascii,0
|
|
je BinRead
|
|
NoTestA:
|
|
mov dx,cx
|
|
mov di,NxtAdd
|
|
mov al,1Ah
|
|
push es
|
|
mov es,Tpa
|
|
repne scasb ; scan for EOF
|
|
pop es
|
|
jne UseAll
|
|
inc RdEof
|
|
inc cx
|
|
UseAll:
|
|
sub dx,cx
|
|
mov cx,dx
|
|
BinRead:
|
|
add cx,NxtAdd
|
|
mov NxtAdd,cx
|
|
cmp cx,BytCnt ; is buffer full?
|
|
jb TestDev ; if not, we may have found eof
|
|
invoke FlshFil
|
|
cmp TermRead,0
|
|
jne CloseSrc ; give up
|
|
jmp short CopyLp
|
|
|
|
TestDev:
|
|
cmp SrcIsDev,0
|
|
je CloseSrc ; if file then EOF
|
|
cmp RdEof,0
|
|
je CopyLp ; on device, go till ^Z
|
|
CloseSrc:
|
|
mov bx,SrcHand
|
|
mov ah,CLOSE
|
|
int 21h
|
|
return
|
|
|
|
|
|
|
|
|
|
CloseDest:
|
|
|
|
; We are called to close the destination.
|
|
; We need to note whether or not there is any internal data left
|
|
; to be flushed out.
|
|
|
|
cmp DestClosed,0
|
|
retnz ; don't double close
|
|
mov al,byte ptr DestSwitch
|
|
invoke SetAsc ; check for b or a switch
|
|
jz BinClos ; on destination
|
|
mov bx,NxtAdd
|
|
;
|
|
;M048 -- TryFlush changes the state of ConCat flag. So, before we append a
|
|
;^Z, let's always flush out. This way if the ConCat flag changes, we will
|
|
;just return without appending a ^Z incorrectly for the first file(since we
|
|
;are concatenating now). Also, in case it is a single file copy, we will
|
|
;anyway write the ^Z out separately. The only drawback is that there is a
|
|
;performance overhead on single ASCII file copies which now always involve
|
|
;2 writes instead of 1 before. Is this really that important?
|
|
;
|
|
;M048; cmp bx,BytCnt ; is memory full?
|
|
;M048; jne PutZ
|
|
|
|
invoke TryFlush ; flush (and double-check for concat)
|
|
je NoConc
|
|
ConChng: ; concat flag changed on us
|
|
stc
|
|
return
|
|
NoConc:
|
|
xor bx,bx
|
|
PutZ:
|
|
push ds
|
|
mov ds,Tpa
|
|
mov word ptr [bx],1Ah ; add EOF mark (ctrl-Z)
|
|
pop ds
|
|
inc NxtAdd
|
|
mov NoWrite,0 ; make sure our ^z gets written
|
|
mov ax,Written
|
|
add ax,NxtAdd
|
|
jc BinClos ; > 1
|
|
cmp ax,1
|
|
je ForgetItJ ; Written = 0 NxtAdd = 1 (the ^Z)
|
|
BinClos:
|
|
invoke TryFlush
|
|
jnz ConChng
|
|
cmp Written,0
|
|
ForgetItJ:
|
|
jne No_Forget ; wrote something
|
|
jmp ForgetIt ; never wrote nothing
|
|
No_Forget:
|
|
mov bx,DestHand
|
|
mov cx,CpTime
|
|
mov dx,CpDate
|
|
cmp Inexact,0 ; copy not exact?
|
|
je DoDClose ; if no, copy date & time
|
|
mov ah,GET_TIME
|
|
int 21h
|
|
shl cl,1
|
|
shl cl,1 ; left justify min in cl
|
|
shl cx,1
|
|
shl cx,1
|
|
shl cx,1 ; hours to high 5 bits, min to 5-10
|
|
shr dh,1 ; divide seconds by 2 (now 5 bits)
|
|
or cl,dh ; and stick into low 5 bits of cx
|
|
push cx ; save packed time
|
|
mov ah,GET_DATE
|
|
int 21h
|
|
sub cx,1980
|
|
xchg ch,cl
|
|
shl cx,1 ; year to high 7 bits
|
|
shl dh,1 ; month to high 3 bits
|
|
shl dh,1
|
|
shl dh,1
|
|
shl dh,1
|
|
shl dh,1 ; most sig bit of month in carry
|
|
adc ch,0 ; put that bit next to year
|
|
or dl,dh ; or low three of month into day
|
|
mov dh,ch ; get year and high bit of month
|
|
pop cx ; get time back
|
|
DoDClose:
|
|
cmp bx,0
|
|
jle CloseDone
|
|
mov ax,(FILE_TIMES shl 8) or 1
|
|
int 21h ; set date and time
|
|
jc Cleanup_Err ;AN022; handle error
|
|
|
|
; See if the destination has *anything* in it.
|
|
; If not, just close and delete it.
|
|
|
|
mov ax,(LSEEK shl 8) + 2 ; seek to EOF
|
|
xor dx,dx
|
|
mov cx,dx
|
|
int 21h
|
|
|
|
; DX:AX is file size
|
|
|
|
or dx,ax
|
|
pushf
|
|
mov ax,(IOCTL SHL 8) + 0 ; get the destination attributes
|
|
int 21h
|
|
push dx ; save them away
|
|
mov ah,CLOSE
|
|
int 21h
|
|
pop dx
|
|
jnc Close_Cont ;AN022; handle error on close
|
|
popf ;AN022; get the flags back
|
|
Cleanup_Err: ;AN022;
|
|
call CleanUpErr ;AN022; attempt to delete the target
|
|
call DestDelete ;AN022; attempt to delete the target
|
|
jmp short FileClosed ;AN022; close the file
|
|
Close_Cont: ;AN022; no error - continue
|
|
popf
|
|
jnz CloseDone
|
|
test dx,80h ; is the destination a device?
|
|
jnz CloseDone ; yes, copy succeeded
|
|
call DestDelete
|
|
jmp short FileClosed
|
|
CloseDone:
|
|
inc FileCnt
|
|
FileClosed:
|
|
inc DestClosed
|
|
Ret50:
|
|
clc
|
|
return
|
|
|
|
|
|
ForgetIt:
|
|
mov bx,DestHand
|
|
call DoDClose ; close the dest
|
|
call DestDelete
|
|
mov FileCnt,0 ; no files transferred
|
|
jmp Ret50
|
|
|
|
|
|
DestDelete:
|
|
mov dx,offset TRANGROUP:DestBuf
|
|
mov ah,UNLINK
|
|
int 21h ; and delete it
|
|
return
|
|
|
|
|
|
|
|
|
|
Source_Set proc near
|
|
|
|
push si
|
|
mov ax,StartEl
|
|
mov si,offset TRANGROUP:ScanBuf ; adjust to copy
|
|
sub ax,si
|
|
mov di,offset TRANGROUP:SrcBuf
|
|
add ax,di
|
|
mov SrcTail,ax
|
|
mov SrcSiz,cl ; save its size
|
|
inc cx ; include the nul
|
|
rep movsb ; save this source
|
|
mov SrcInfo,bh ; save info about it
|
|
pop si
|
|
mov ax,bp ; switches so far
|
|
invoke SetAsc ; set a,b switches accordingly
|
|
invoke Switch ; get any more switches on this arg
|
|
invoke SetAsc ; set
|
|
return
|
|
|
|
Source_Set endp
|
|
|
|
|
|
|
|
|
|
;****************************************************************
|
|
;*
|
|
;* ROUTINE: CleanupErr
|
|
;*
|
|
;* FUNCTION: Issues extended error message for destination
|
|
;* if not alreay issued
|
|
;*
|
|
;* INPUT: return from INT 21
|
|
;*
|
|
;* OUTPUT: none
|
|
;*
|
|
;****************************************************************
|
|
|
|
CleanupErr proc near ;AN022;
|
|
|
|
cmp Msg_Flag,0 ;AN022; have we already issued a message?
|
|
jnz CleanupErr_Cont ;AN022; yes - don't issue duplicate error
|
|
invoke Set_Ext_Error_Msg ;AN022; set up error message
|
|
mov String_Ptr_2,offset TRANGROUP:DestBuf ;AN022; get address of failed string
|
|
mov Extend_Buf_Sub,ONE_SUBST ;AN022; put number of subst in control block
|
|
invoke Std_EPrintF ;AN022; issue the error message
|
|
|
|
CleanupErr_Cont: ;AN022;
|
|
ret ;AN022; return to caller
|
|
|
|
CleanupErr endp ;AN022;
|
|
|
|
|
|
TRANCODE ends
|
|
end
|
|
|