	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
