 	NAME	msster
; File MSSTER.ASM
	include mssdef.h
;	Copyright (C) 1982, 1995, Trustees of Columbia University in the 
;	City of New York.  The MS-DOS Kermit software may not be, in whole 
;	or in part, licensed or sold for profit as a software product itself,
;	nor may it be included in or distributed with commercial products
;	or otherwise distributed by commercial concerns to their clients 
;	or customers without written permission of the Office of Kermit 
;	Development and Distribution, Columbia University.  This copyright 
;	notice must not be removed, altered, or obscured.
;
; Edit history
; 12 Jan 1995 version 3.14
; Last edit
; 12 Jan 1995
; 27 August 1992 version 3.13
; 6 Sept 1991 version 3.11

	public	clscpt, defkey, clscpi, ploghnd, sloghnd, tloghnd
	public  dopar, shokey, cptchr, pktcpt, targ, replay, repflg
	public	kbdflg, shkadr, telnet, ttyact, write, dec2di, caplft
	public	cnvlin, katoi, decout, valout, atoi, cnvstr
	public	pntchr, pntflsh, pntchk, prnhand, prnopen
	public	vfopen, vfread

braceop	equ	7bh			; opening curly brace
bracecl	equ	7dh			; closing curly brace

data 	segment
	extrn	flags:byte, trans:byte, diskio:byte, portval:word
	extrn	rdbuf:byte, dosnum:word, filtst:byte, prnname:byte
	extrn	comand:byte, kstatus:word, flowon:byte, flowoff:byte
	extrn	cardet:byte

targ	termarg	<0,1,cptchr,0,parnon>
crlf    db      cr,lf,'$'
tmsg1	db	cr,lf,'(Connecting to host, type $' 
tmsg3	db	' C to return to PC)',cr,lf,cr,lf,cr,lf,'$'
erms21	db	cr,lf,'?Cannot start the connection.$'
erms25	db	cr,lf,'?Input must be numeric$' 
erms22	db	cr,lf,'?No open logging file$'
erms23	db	'*** Error writing session log, suspending capture ***$'
erms24	db	cr,lf,'?Error writing Packet log$'
erms26	db	' *** PRINTER IS NOT READY ***  press R to retry'
	db	' or D to discard printing:  $'
erms27	db	cr,lf
	db	'? Carrier signal not detected on this serial port.'
	db	cr,lf
	db    '  Use the DIAL command, SET CARRIER OFF, or check your modem.$'

esctl	db	'Control-$'
repflg	db	0		; REPLAY or SET TERM REPLAY filespec flag
reperr	db	cr,lf,'?File not found$'	; for REPLAY command
noterm	db	cr,lf,'Connect mode is not built into this Kermit$'
vfopbad	db	cr,lf,'?Cannot open file $'	; filename follows
vfclbad	db	cr,lf,'?Cannot close file$'
vfoptwice db	cr,lf,'?File is already open$'
vfnofile db	cr,lf,'?File is not open$'
vfrbad	db	cr,lf,'?Error while reading file$'
vfwbad	db	cr,lf,'?Error while writing file$'
vfrdbad	db	cr,lf,'?more parameters are needed$'
vfrhandle dw	-1			; READ FILE handle (-1 = invalid)
vfwhandle dw	-1			; WRITE FILE handle

opntab	db	3			; OPEN FILE table
	mkeyw	'Read',1
	mkeyw	'Write',2
	mkeyw	'Append',3

inthlp db cr,lf,' ', 22 dup ('-'),' Special keys within Connect mode '
       db	22 dup ('-')
       db cr,lf,'                           numeric keypad keys:'
       db cr,lf,'  Pg Up/Dn  roll screen vertically   Ctrl Pg Up/Dn  roll'
       db	' screen one line '
       db cr,lf,'  Home/End  roll to start/end screen Ctrl End screen dump'
       db cr,lf,'  - (minus) toggle status/mode line'
       db cr,lf,'                           other key combinations:'
       db cr,lf,'  Alt-b  send a BREAK                Alt-n  next active'
       db	' Telnet session'
       db cr,lf,'  Alt-c  start Compose sequence      Alt-x  exit emulator'
       db cr,lf,'  Alt-h  show this menu              Alt-z  network session'
       db	' hold'
       db cr,lf,'  Alt-=  reset emulator              Alt--  toggle terminal'
       db	' type'
       db cr,lf,'  Ctrl Prtscrn  toggle printing'
       db cr,lf,'  Additional commands are escape character '
inthlpc db '  '
       db	' followed by special characters.'
       db cr,lf,' ',78 dup ('-')
       db cr,lf,'  7n1 = SET TERM BYTE 7, SET PARITY NONE, 1 stop bit'
       db cr,lf,'  8n1 = SET TERM BYTE 8, SET PARITY NONE, 1 stop bit'
       db cr,lf,'  7e1 = SET PARITY EVEN, 1 stop bit   7s1 = SET PARITY SPACE'
       db	', 1 stop bit'
       db cr,lf,'  7o1 = SET PARITY ODD,  1 stop bit   7m1 = SET PARITY MARK'
       db	',  1 stop bit'
       db cr,lf,'  press a key to exit this menu'
       db	0

intqry db cr,lf,' ',20 dup ('-'),' Single character commands active now ' 
       db	20 dup ('-')
       db cr,lf,'  ?  Show this menu                  F  Dump screen to file'
       db cr,lf,'  C  Close the emulator              P  Push to DOS'
       db cr,lf,'  S  Status of the connection        Q  Quit logging'
       db cr,lf,'  M  Toggle mode line                R  Resume logging'
       db cr,lf,'  B  Send a Break                    0  Send a null'
       db cr,lf,'  L  Send a long 1.8 s Break         H  Hangup phone'
       db cr,lf,'  A  Send Telnet "Are You There"     I  Send Telnet' 
       db	' "Interrupt Process"' 
       db cr,lf,'  Typing the escape character '
intqryc db '  '					; where escape char glyphs go
       db	' will send it to the host'
       db cr,lf,'  press space bar to exit this menu'
       db 0

intprm	db	'Command> $'
intclet	db	'B','C','F','H','L'	; single letter commands
	db	'M','P','Q','R','S'	; must parallel dispatch table intcjmp
	db	'?','0','A','I'
numlet	equ	$ - intclet		; number of entries
	even
ifndef	no_terminal
intcjmp	dw	intchb,intchc,intchf,intchh,intchl
	dw	intchm,intchp,intchq,intchr,intchs
	dw	intchu,intchn,intayt,inttip
endif	; no_terminal

prnhand	dw	4		; printer file handle (4 = DOS default)

	even
ploghnd	dw	-1		; packet logging handle
sloghnd	dw	-1		; session logging handle
tloghnd	dw	-1		; transaction logging handle

clotab	db	6
	mkeyw	'READ-FILE',4001h
	mkeyw	'WRITE-FILE',4002h
	mkeyw	'All-logs',logpkt+logses+logtrn
	mkeyw	'Packets',logpkt
	mkeyw	'Session',logses
	mkeyw	'Transaction',logtrn

clseslog db	cr,lf,' Closing Session log$'
clpktlog db	cr,lf,' Closing Packet log$'
cltrnlog db	cr,lf,' Closing Transaction log$'

writetab db	5			; Write command log file types
	mkeyw	'FILE',4002h		; FILE
	mkeyw	'Packet',logpkt
	mkeyw	'Screen',80h		; unused value, to say screen
	mkeyw	'Session',logses
	mkeyw	'Transaction',logtrn

sttmsg	db	cr,lf,'Press space to continue ...$'
kbdflg	db	0			; non-zero means char here from Term
ttyact	db	0			; Connect mode active, if non-zero
shkadr	dw	0			; offset of replacement Show Key cmd
nbase	dw	10			; currently active number base
numset	db	'0123456789ABCDEF'	; number conversion alphabet
temp	dw	0
temp1	dw	0
tmp	db	0
pktlft	dw	cptsiz		; number free bytes left
caplft	dw	cptsiz		; number free bytes left
data	ends

data1	segment
rephlp	db	'name of file to playback$'
clohlp	db	cr,lf,' READ-FILE or WRITE-FILE, or the following log files:'
	db	cr,lf,' All-logs, Packets, Session, Transaction$'
msgtxt	db	'text to be written$'
vfophlp	db	'Filename$'
vfrdmsg	db	'name of variable  into which to read a line from file$'


pktbuf	db	cptsiz dup (0)	; packet logging buffer
pktbp	dw	pktbuf		; buffer pointer to next free byte
capbuf	db	cptsiz dup (0)	; session logging buffer
capbp	dw	capbuf		; buffer pointer to next free byte
prnbuf	db	cptsiz dup (0)	; printer buffer
pntptr	dw	prnbuf		; pointer to next free byte
data1	ends

code	segment
	extrn 	comnd:near, outchr:near, stat0:near, iseof:near
	extrn	strlen:near, pcwait:near, isfile:near
	extrn	beep:near, puthlp:near, serhng:near
	extrn	serini:near, serrst:near, sendbr:near, putmod:near
	extrn	fpush:near,  sendbl:near, prtasz:near
	extrn	trnmod:near, dodecom:near
ifndef	no_terminal
	extrn	dumpscr:near,term:near
endif	; no_terminal
	assume	cs:code, ds:data, es:nothing

; the show key command
shokey	proc	near
	cmp	shkadr,0		; keyboard translator present?
	je	shokey1			; e = no, use regular routines
	mov	bx,shkadr		; get offset of replacement routine
	jmp	bx			; and execute it rather than us
shokey1:clc
	ret
shokey	endp
; enter with ax/scan code to define, si/ pointer to definition, cx/ length
; of definition.  Defines it in definition table. Obsolete.
defkey	proc	near
	ret
defkey	endp


; This is the CONNECT command
ifdef	no_terminal
TELNET 	PROC	NEAR
	mov	ah,cmeol
	call	comnd			; get a confirm
	jnc	teln1			; nc = success
	ret
teln1:
	mov	ah,prstr
	mov	dx,offset noterm
	int	dos
	clc
	ret
TELNET	ENDP
endif	; no_terminal
 
ifndef	no_terminal
TELNET 	PROC	NEAR
	mov	ah,cmeol
	call	comnd			; get a confirm
	jnc	teln1			; nc = success
	ret
teln1:	cmp	repflg,0		; REPLAY?
	jne	teln1d			; ne = yes
	call	serini			; ensure port is inited now
	jnc	teln1a			; nc = success
	test	flags.remflg,dquiet	; quiet display mode?
	jnz	teln1b			; nz = yes. Don't write to screen
	mov	dx,offset erms21	; say cannot start connection
	mov	ah,prstr
	int	dos
teln1b:	or	kstatus,ksgen		; general command failure
	ret
teln1a:	cmp	flags.comflg,'F'	; Fossil port?
	je	teln1c			; e = yes
	cmp	flags.comflg,'4'	; using real or Bios serial ports?
	ja	teln1d			; a = no
teln1c:	cmp	flags.carrier,0		; want CD on now?
	je	teln1d			; e = no
	test	cardet,80h		; CD on?
	jnz	teln1d			; nz = yes
	mov	ah,prstr		; complain on screen
	mov	dx,offset erms27
	int	dos
	stc				; fail
	ret
teln1d:	cmp	flags.vtflg,0		; emulating a terminal?
	jne	teln2			; ne= yes, no wait necessary
	mov	ah,prstr
	mov	dx,offset crlf		; output a crlf
	int	dos
	call	domsg			; reassure user
	mov	ax,2000			; two seconds
	call	pcwait			; pause
teln2:	xor	al,al			; initial flags
	mov	ttyact,1		; say telnet is active
	cmp	flags.vtflg,0		; emulating a terminal?
	je	teln3			; e = no, say mode line is to be off
	cmp	flags.modflg,0		; mode line enabled?
	jne	tel010			; ne = yes
teln3:	or	al,modoff		; no, make sure it stays off

tel010:	test	flags.debug,logses	; debug mode?
	jz	tel0			; z = no, keep going
	or	al,trnctl		; yes, show control chars
tel0:	cmp	flags.vtflg,0		; emulating a terminal?
	je	tel1			; e = no
	or	al,emheath		; say emulating some kind of terminal
tel1:	mov	bx,portval
	cmp	[bx].ecoflg,0		; echoing?
	jz	tel2			; z = no
	or	al,lclecho		; turn on local echo
tel2:	mov	targ.flgs,al		; store flags
	mov	ah,flags.comflg		; COMs port identifier
	mov	targ.prt,ah		; Port 1 or 2, etc
	mov	ah,[bx].parflg		; parity flag
	mov	targ.parity,ah
	mov	ax,[bx].baud		; baud rate identifier
	mov	targ.baudb,al
	xor	ah,ah
	test	flags.capflg,logses	; select session logging flag bit
	jz	tel3			; z = no logging
	mov	ah,capt			; set capture flag
tel3:	or	targ.flgs,ah
	jmp	short tem1

TEM:	call	serini			; init serial port
	jnc	tem1			; nc = success
	mov	ttyact,0		; say we are no longer active
	clc
	ret				; and exit Connect mode

tem1:	mov	dx,offset crlf		; give user an indication that we are
	mov	ah,prstr		; entering terminal mode
	int	dos
	mov	ax,offset targ		; point to terminal arguments
	call	term			; call the main Terminal procedure
	mov	al,kbdflg		; get the char from Term, if any
	mov	kbdflg,0		; clear	the flag
	or	al,al			; was there a char from Term?
	jnz	intch2			; nz = yes, else ask for one from kbd

intchar:call	iseof			; stdin at eof?
	jnc	intch1			; nc = not eof, get more
	mov	al,'C'			; use C when file is empty
	jmp	intchc			;  to provide an exit
intch1:	cmp	al,0			; asking for help?
	jne	intch1b			; ne = no
	mov	al,' '
	mov	ah,trans.escchr
	cmp	ah,' '			; printable now?
	jae	intch1a			; ae = yes
	mov	al,'^'
	add	ah,40h			; make control visible
intch1a:mov	word ptr intqryc,ax	; store escape char code
	mov	ax,offset intqry	; '?' get help message
	call	puthlp			; write help msg
	mov	dx,offset intprm
	mov	ah,prstr		; show prompt
	int	dos
intch1b:mov	ah,0ch			; clear Bios keyboard buffer and do
	mov	al,coninq		;  read keyboard, no echo
	int	dos			; get a char
	or	al,al			; scan code indicator?
	jnz	intch1c			; nz = no, ascii
	mov	ah,coninq		; read and discard scan code
	int	dos
	jmp	short intch1		; try again
intch1c:cmp	al,'?'			; want to see menu again?
	jne	intch2			; ne = no
	xor	al,al			; prep for menu display
	jmp	short intchar

intch2:	mov	flags.cxzflg,0		; prevent Control-C carryover
	cmp	al,' '			; space?
	je	tem			; e = yes, ignore it
	cmp	al,cr			; check ^M (cr) against plain ascii M
	je	tem			; exit on cr
	cmp	al,trans.escchr		; is it the escape char?
	jne	intch3			; ne = no
	mov	ah,al
	call	outchr
	jmp	tem			; return, we are done here
intch3:	push	es
	push	ds
	pop	es
	mov	di,offset intclet	; command letters
	mov	cx,numlet		; quantity of them
	cmp	al,' '			; control code?
	jae	intch3a			; ae = no
	or	al,40H			; convert control chars to printable
intch3a:cmp	al,96			; lower case?
	jb	intch3b			; b = no
	and	al,not (20h)		; move to upper case
intch3b:cld
	repne	scasb			; find the matching letter
	pop	es
	jne	intch4			; ne = not found, beep and get another
	dec	di			; back up to letter
	sub	di,offset intclet	; get letter number
	shl	di,1			; make it a word index
	jmp	intcjmp[di]		; dispatch to it
intch4:	call	beep			; say illegal character
	jmp	intchar

intayt:	mov	ah,255			; 'I' Telnet Are You There
	call	outchr			; send IAC (255) AYT (246)
	mov	ah,246
	call	outchr
	jmp	tem

intchb:	call	sendbr			; 'B' send a break
	jmp	tem			; And return

intchc:	mov	ttyact,0		; 'C' say we are no longer active
	clc				; and exit Connect mode
	ret

intchf:	call	dumpscr			; 'F' dump screen, use msy routine
	jmp	tem			; and return

intchh:	call	serhng			; 'H' hangup phone
	call	serrst			; turn off port
	jmp	tem

intchl:	call	sendbl			; 'L' send a long break
	jmp	tem

inttip:	mov	ah,255			; 'I' Telnet Interrrupt Process
	call	outchr			; send IAC (255) IP (244)
	mov	ah,244
	call	outchr
	jmp	tem

intchm:	cmp	flags.modflg,1		; 'M' toggle mode line, enabled?
	jne	intchma			; ne = no, leave it alone
	xor	targ.flgs,modoff	; enabled, toggle its state
intchma:jmp	tem			; and reconnect

intchp:	call	fpush			; 'P' push to DOS
	mov	dx,offset sttmsg	; say we have returned
	mov	ah,prstr
	int	dos
	jmp	short intchsb		; wait for a space

intchq:	and	targ.flgs,not capt	; 'Q' suspend session logging
	jmp	tem			; and resume

intchr:	test	flags.capflg,logses	; 'R' resume logging. Can we capture?
	jz	intchr1			; z = no
	or	targ.flgs,capt		; turn on session logging flag
intchr1:jmp	tem			; and resume

intchs:	call	stat0			; 'S' status, call stat0
	mov	dx,offset sttmsg
	mov	ah,prstr
	int	dos
intchsa:call	iseof			; is stdin at eof?
	jnc	intchsb			; nc = not eof, get more
	jmp	tem			; resume if EOF
intchsb:mov	ah,coninq		; console input, no echo
	int	dos
	cmp	al,' '			; space?
	jne	intchsa
	jmp	tem

intchu:	mov	ah,trans.escchr
	cmp	ah,' '			; printable now?
	jae	intchu1			; ae = yes
	mov	al,'^'
	add	ah,40h			; make control visible
intchu1:mov	word ptr inthlpc,ax	; store escape char code
	mov	ax,offset inthlp	; help message
	call	puthlp			; write help msg
	mov	ah,0ch			; clear Bios keyboard buffer and do
	mov	al,coninq		;  read keyboard, no echo
	int	dos			; get a char
	or	al,al			; scan code indicator?
	jnz	intchu2			; nz = no, ascii
	mov	ah,coninq		; read and discard scan code
	int	dos
intchu2:jmp	tem			; try again

intchn:	xor	ah,ah			; '0' send a null
	call	outchr
	jmp	tem
TELNET  ENDP

; Reassure user	about connection to the host. Tell him what escape sequence
; to use to return

DOMSG	PROC	NEAR
	mov	ah,prstr
	mov	dx,offset tmsg1
	int	dos
	call	escprt
	mov	ah,prstr
	mov	dx,offset tmsg3
	int	dos
	ret
DOMSG	ENDP

endif	; no_terminal

; print	the escape character in readable format.  

ESCPRT	PROC	NEAR
	mov	dl,trans.escchr
	cmp	dl,' '
	jge	escpr2
	push	dx
	mov	ah,prstr
	mov	dx,offset esctl
	int	dos
	pop	dx
	add	dl,040H		; Make it printable
escpr2:	mov	ah,conout
	int	dos
	ret
ESCPRT	ENDP
 

; Set parity for character in Register AL

dopar:	push	bx
	mov	bx,portval
	mov	bl,[bx].parflg		; get parity flag byte
	cmp	bl,parnon		; No parity?
	je	parret			; Just return
	and	al,07FH			; Strip parity. Same as Space parity
	cmp	bl,parspc		; Space parity?
	je	parret			; e = yes, then we are done here
	cmp	bl,parevn		; Even parity?
	jne	dopar0			; ne = no
	or	al,al
	jpe	parret			; pe = even parity now
	xor	al,080H			; Make it even parity
	jmp	short parret
dopar0:	cmp	bl,parmrk		; Mark parity?
	jne	dopar1			; ne = no
	or	al,080H			; Turn on the parity bit
	jmp	short parret
dopar1:	cmp	bl,parodd		; Odd parity?	
	or	al,al
	jpo	parret			; Already odd, leave it
	xor	al,080H			; Make it odd parity
parret:	pop	bx
	ret

; REPLAY filespec  through terminal emulator
replay	proc	near
	mov	dx,offset rdbuf		; place for filename
	mov	bx,offset rephlp	; help
	mov	repflg,0		; clear the replay active flag
	mov	ah,cmword		; get filename
	call	comnd
	jc	replay2			; c = failure
	mov	ah,cmeol		; get an EOL confirm
	call	comnd
	jc	replay2			; c = failure
ifndef	no_terminal
	mov	ah,open2		; open file
	xor	al,al			; open readonly
	cmp	byte ptr dosnum+1,2	; above DOS 2?
	jna	replay1			; na = no, so no shared access
	mov	al,0+40h		; open readonly, deny none
replay1:mov	dx,offset rdbuf		; asciiz filename
	int	dos
	jnc	replay3			; nc = success
	mov	ah,prstr
	mov	dx,offset reperr	; Cannot open that file
	int	dos
	clc
	ret
replay3:mov	diskio.handle,ax	; file handle
	mov	repflg,1		; set replay flag
	call	telnet			; enter Connect mode
	mov	bx,diskio.handle
	mov	ah,close2		; close the file
	int	dos
endif	; no_terminal
	mov	repflg,0		; clear the flag
	clc
replay2:ret
replay	endp

cptchr	proc	near			; session capture routine, char in al
	test	flags.capflg,logses	; session logging active now?
	jz	cptch1			; z = no
	push	di
	push	es
	mov	di,data1		; seg of capbp and capbuf
	mov	es,di
	cld
	mov	di,es:capbp		; buffer pointer
	stosb
	inc	es:capbp
	pop	es
	pop	di
	dec	caplft			; decrement chars remaining
	jg	cptch1			; more room, forget this part
	call	cptdmp			; dump the info
cptch1:	ret
cptchr	endp

cptdmp	proc	near			; empty the capture buffer
	push	ax
	push	bx
	push	cx
	push	dx
	mov	bx,sloghnd		; get file handle
	or	bx,bx			; is file open?
	jle	cptdm1			; le = no, skip it
	mov	cx,cptsiz		; original buffer size
	sub	cx,caplft		; minus number remaining
	jl	cptdm2			; means error
	jcxz	cptdm1			; z = nothing to do
	push	ds
	mov	dx,data1		; seg of capbuf
	mov	ds,dx
	mov	dx,offset capbuf	; the capture routine buffer
	mov	ah,write2		; write with filehandle
	int	dos			; write out the block
	pop	ds
	jc	cptdm2			; carry set means error
	cmp	ax,cx			; wrote all?
	jne	cptdm2			; no, an error
	push	es
	mov	dx,data1		; seg of capbuf and capbp
	mov	es,dx
	mov	es:capbp,offset capbuf
	pop	es
	mov	caplft,cptsiz		; init buffer ptr & chrs left
	clc
	jmp	short cptdm1
cptdm2:	and	targ.flgs,not capt	; so please stop capturing
	and	flags.capflg,not logses	; deselect session logging flag bit
	mov	dx,offset erms23	; tell user the bad news
	call	putmod			; write on mode line
	stc
cptdm1:	pop	dx
	pop	cx
	pop	bx
	pop	ax
	ret
cptdmp	endp

pktcpt	proc	near			; packet log routine, char in al
	test	flags.capflg,logpkt	; logging packets now?
	jz	pktcp1			; z = no
	push	di
	push	es
	mov	di,data1		; seg of pktbuf and pktbp
	mov	es,di
	mov	di,es:pktbp		; buffer pointer
	cld
	stosb				; store char in buffer
	inc	es:pktbp		; move pointer to next free byte
	pop	es
	pop	di
	dec	pktlft			; decrement chars remaining
	jg	pktcp1			; g = more room, forget this part
	call	pktdmp			; dump the info
pktcp1:	ret
pktcpt	endp

pktdmp	proc	near			; empty the capture buffer
	push	ax
	push	bx
	push	cx
	push	dx
	mov	bx,ploghnd		; get file handle
	or	bx,bx			; is file open?
	jle	cptdm1			; le = no, skip it
	mov	cx,cptsiz		; original buffer size
	sub	cx,pktlft		; minus number remaining
	jl	pktdm2			; l means error
	jcxz	pktdm1			; z = nothing to do
	push	ds
	mov	dx,data1		; seg of pktbuf
	mov	ds,dx
	mov	dx,offset pktbuf	; the capture routine buffer
	mov	ah,write2		; write with filehandle
	int	dos			; write out the block
	pop	ds
	jc	pktdm2			; carry set means error
	cmp	ax,cx			; wrote all?
	jne	pktdm2			; ne = no, error
	push	es
	mov	dx,data1		; seg of pktbuf
	mov	es,dx
	mov	es:pktbp,offset pktbuf
	pop	es
	mov	pktlft,cptsiz		; init buffer ptr & chrs left
	jmp	short pktdm1
pktdm2:	and	flags.capflg,not logpkt	; so please stop capturing
	mov	dx,offset erms24	; tell user the bad news
	mov	ah,prstr
	int	dos
	call	clscp4			; close the packet log
pktdm1:	pop	dx
	pop	cx
	pop	bx
	pop	ax
	ret
pktdmp	endp

; CLOSE command

clscpt	proc	near
	mov	ah,cmkey		; get kind of file to close
	mov	dx,offset clotab	; close table
	mov	bx,offset clohlp	; help
	call	comnd
       	jc	clscp2			; c = failure
	mov	temp,bx
	mov	ah,cmeol
	call	comnd
       	jc	clscp2			; c = failure
	mov	kstatus,kssuc		; success status thus far
	mov	bx,temp
	cmp	bh,40h			; READ-FILE or WRITE-FILE?
	jne	clscp0			; ne = no
	mov	ax,bx
	jmp	vfclose			; close FILE, pass kind in AX
clscp0:	cmp	bx,logpkt+logses+logtrn	; close all?
	je	clscpi			; e = yes
	cmp	bx,logpkt		; just packet?
	je	clscp4
	cmp	bx,logses		; just session?
	je	clscp6
	cmp	bx,logtrn		; just session?
	jne	clscp1			; ne = no
	jmp	clscp8
clscp1:	mov	dx,offset erms22	; say none active
	mov	ah,prstr
	int	dos
	clc
	ret
clscp2:	mov	kstatus,ksgen		; general cmd failure status
	stc
	ret
					; CLSCPI called at Kermit exit
CLSCPI:	mov	bx,portval
	mov	[bx].flowc,0		; set no flow control so no sending it
	call	pntflsh			; flush PRN buffer
	call	clscp4			; close packet log
	call	clscp6			; close session log
	call	clscp8			; close transaction log
	mov	al,2			; close WRITE FILE log
	call	vfclose
	clc				; return success
	ret

clscp4:	push	bx			; PACKET LOG
	mov	bx,ploghnd		; packet log handle
	or	bx,bx			; is it open?
	jle	clscp5			; e = no
	call	pktdmp			; dump buffer
	mov	ah,close2
	int	dos
	cmp	flags.takflg,0		; ok to echo?
	je	clscp5			; e = no
	mov	ah,prstr
	mov	dx,offset clpktlog	; tell what we are doing
	int	dos
clscp5:	mov	ploghnd,-1		; say handle is invalid
	pop	bx
	and	flags.capflg,not logpkt	; say this log is closed
	ret

clscp6:	push	bx			; SESSION LOG
	mov	bx,sloghnd		; session log handle
	or	bx,bx			; is it open?
	jle	clscp7			; e = no
	call	cptdmp			; dump buffer
	mov	ah,close2
	int	dos
	cmp	flags.takflg,0		; ok to echo?
	je	clscp7			; e = no
	mov	ah,prstr
	mov	dx,offset clseslog	; tell what we are doing
	int	dos
clscp7:	mov	sloghnd,-1		; say handle is invalid
	pop	bx
	and	flags.capflg,not logses	; say this log is closed
	ret

clscp8:	push	bx			; TRANSACTION LOG
	mov	bx,tloghnd		; transaction log handle
	or	bx,bx			; is it open?
	jle	clscp9			; e = no
	mov	ah,close2
	int	dos
	cmp	flags.takflg,0		; ok to echo?
	je	clscp9			; e = no
	mov	ah,prstr
	mov	dx,offset cltrnlog	; tell what we are doing
	int	dos
clscp9:	mov	tloghnd,-1		; say handle is invalid
	pop	bx
	and	flags.capflg,not logtrn	; say this log is closed
	ret
clscpt	endp

; Print on PRN the char in register al. On success return with C bit clear.
; On failure do procedure pntchk and return its C bit (typically C set).
; Uses buffer dumpbuf (screen dump).
pntchr	proc	near
	push	es
	push	ax
	mov	ax,data1		; segment of pntptr and prnbuf
	mov	es,ax
	cmp	es:pntptr,offset prnbuf+cptsiz ; buffer full yet?
	pop	ax
	pop	es
	jb	pntchr1			; b = no
	call	pntflsh			; flush buffer now
	jnc	pntchr1			; nc = success
	ret				; c = fail, discard char
pntchr1:push	es
	push	bx
	mov	bx,data1		; segment of pntptr and prnbuf
	mov	es,bx
	mov	bx,es:pntptr		; pointer to next open slot
	mov	es:[bx],al		; store the character
	inc	bx			; update pointer
	mov	es:pntptr,bx		; save pointer
	pop	bx
	pop	es
	clc				; clear carry bit
	ret
pntchr	endp

; Flush printer buffer. Return carry clear if success.
; On failure do procedure pntchk and return its C bit (typically C set).
; Uses buffer dumpbuf (screen dump).
pntflsh	proc	near
	push	es
	push	ax
	mov	ax,data1		; segment of pntptr and prnbuf
	mov	es,ax
	cmp	es:pntptr,offset prnbuf	; any text in buffer?
	pop	ax
	pop	es
	ja	pntfls1			; a = yes
	clc
	ret				; else nothing to do
pntfls1:cmp	prnhand,0		; is printer handle valid?
	jg	pntfls2			; g = yes
	push	es
	push	ax
	mov	ax,data1		; segment of pntptr and prnbuf
	mov	es,ax
	mov	es:pntptr,offset prnbuf
	pop	ax
	pop	es
	clc				; omit printing, quietly
	ret
pntfls2:push	ax
	push	bx
	push	cx
	push	dx
	push	si
	push	di
	push	es
	mov	ah,flowoff		; get flow control char
	or	ah,ah			; flow control active?
	jz	pntfls3			; z = no, not using xoff
	call	outchr			; output xoff (ah), no echo
pntfls3:mov	bx,prnhand		; file handle for DOS printer PRN
	push	DS			; about to change DS
	mov	dx,data1		; segment of prnbuf
	mov	ds,dx			; set DS
	mov	dx,offset prnbuf	; start of buffer
	mov	cx,ds:pntptr
	sub	cx,dx			; cx = current byte count
pntfls4:push	cx
	push	dx
	mov	cx,1
	mov	ah,write2
	int	dos			; write buffer to printer
	pop	dx
	pop	cx
	jc	pntfls5			; c = call failed
	cmp	ax,1			; did we write it?
	jne	pntfls5			; ne = no, dos critical error
	inc	dx			; point to next char
	loop	pntfls4
	mov	ds:pntptr,offset prnbuf	; reset buffer pointer
	pop	DS			; restore DS
	clc				; declare success
	jmp	pntfls11
pntfls5:mov	si,dx			; address of next char to be printed
	mov	di,offset prnbuf	; start of buffer
	sub	dx,di			; dx now = number successful prints
	mov	cx,ds:pntptr
	sub	cx,si			; count of chars to be printed
	jle	pntfls6
	mov	ax,ds
	mov	es,ax
	cld
	rep	movsb			; copy unwritten to start of buffer
pntfls6:sub	ds:pntptr,dx		; move back printer pointer by ok's
	pop	DS			; restore DS

pntfls7:mov	dx,offset erms26	; printer not ready, get user action
	call	putmod			; write new mode line
	call	beep			; make a noise
	mov	ah,0ch			; clear DOS typeahead buffer
	mov	al,1			; read from DOS buffer
	int	dos
	or	al,al			; Special key?
	jnz	pntfls8			; nz = no, consume
	mov	al,1			; consume scan code
	int	dos
	jmp	short pntfls7		; try again

pntfls8:and	al,not 20h		; lower to upper case quickie
	cmp	al,'R'			; Retry?
	jne	pntfls8a		; ne = no
	call	trnmod			; toggle mode line
	call	trnmod			; back to same state as before
	jmp	pntfls3			; go retry
pntfls8a:cmp	al,'D'			; Discard printing?
	jne	pntfls7			; ne = no, try again
	mov	bx,prnhand
	cmp	bx,4			; stdin/stdout/stderr/stdaux/stdprn?
	jbe	pntfls9			; be = yes, always available
	mov	ah,close2		; close this file
	int	dos
pntfls9:mov	bx,offset prnname	; name of printer file
	mov	word ptr [bx],'UN'	; set to NUL<0>
	mov	word ptr [bx+2],'L'+0
	push	es
	mov	ax,data1		; seg for pntptr
	mov	es,ax
	mov	es:pntptr,offset prnbuf	; reset pointer
	pop	es
	mov	prnhand,-1		; declare handle invalid
pntfls10:call	trnmod			; toggle mode line
	call	trnmod			; back to same state as before
	stc				; declare failure
pntfls11:pushf
	mov	ah,flowon
	or	ah,ah			; flow control active?
	jz	pntfls12		; z = no, not using xon
	call	outchr			; output xon (al), no echo
pntfls12:popf
	pop	es
	pop	di
	pop	si
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	ret				; nc = success
pntflsh	endp

; Check for PRN (DOS's printer) being ready. If ready, return with C clear
; Otherwise, write Not Ready msg on mode line and return with C bit set.
; N.B. DOS Critical Error will occur here if PRN is not ready.  [jrd]
pntchk	proc	near
	push	dx
	push	cx
	push	ax
	mov	cx,10			; ten retries before declaring error
	cmp	prnhand,0		; printer handle valid?
	jle	pntchk2			; le = no, invalid
pntchk0:push	bx
	mov	bx,prnhand		; file handle
	mov	ah,ioctl		; get printer status, via DOS
	mov	al,7			; status for output
	int	dos
	pop	bx
	jc	pntchk1			; c = call failed
	cmp	al,0ffh			; code for Ready?
	je	pntchk3			; e = yes, assume printer is ready
pntchk1:push	cx			; save counter, just in case
	mov	ax,100			; wait 100 millisec
	call	pcwait
	pop	cx
	loop	pntchk0			; and try a few more times
					; get here when printer is not ready
pntchk2:pop	ax
	pop	cx
	pop	dx
	stc				; say printer not ready
	ret
pntchk3:pop	ax
	pop	cx
	pop	dx
	clc				; say printer is ready
	ret
pntchk	endp


prnopen	proc	near
	push	ax
	push	bx
	push	cx
	push	dx
	mov	prnhand,4		; preset default handle
	mov	dx,offset prnname	; name of disk file, from msssho
	mov	ax,dx			; where isfile wants name ptr
	call	isfile			; what kind of file is this?
	jc	prnop3			; c = no such file, create it
	test	byte ptr filtst.dta+21,1fh ; file attributes, ok to write?
	jnz	prnop2			; nz = no
	mov	al,1			; writing
	mov	ah,open2		; open existing file
	int	dos
	jc	prnop2			; c = failure
	mov	prnhand,ax		; save file handle
	mov	bx,ax			; handle for DOS
	mov	ah,ioctl
	mov	al,0			; get info
	int	dos
	or	dl,20h			; turn on binary mode
	xor	dh,dh
	mov	ah,ioctl
	mov	al,1			; set info
	int	dos
	mov	cx,0ffffh		; setup file pointer
	mov	dx,-1			; and offset
	mov	al,2			; move to eof minus one byte
	mov	ah,lseek		; seek the end
	int	dos
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	clc
	ret
prnop2:	stc
	ret
prnop3:	test	filtst.fstat,80h	; access problem?
	jnz	prnop2			; nz = yes
	mov	ah,creat2		; file did not exist
	mov	cx,20h			; attributes, archive bit
	int	dos
	mov	prnhand,ax		; save file handle
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	ret				; may have carry set
prnopen	endp

; worker: copy line from si to di, first removing trailing spaces, second
; parsing out curly braced strings, then third converting \{b##} in strings
; to binary numbers. Returns carry set if error; else carry clear, with byte
; count in cx. Braces are optional but must occur in pairs.
; Items which cannot be converted to legal numbers are copied verbatium
; to the output string (ex: \{c}  is copied as \{c}  but \{x0d} is hex 0dh).
; '\\' is written as '\'
cnvlin	proc	near
	push	ax
	push	si			; source ptr
	push	di			; destination ptr
	push	es			; end of save regs
	push	ds			; move ds into es
	pop	es			; use data segment for es:di
	call	cnvstr			; parse curly braces
	xor	cx,cx			; initialize returned byte count
	cld
cnvln1:	cmp	byte ptr [si],0		; at end of string?
	je	cnvln2			; e = yes, exit
	mov	ax,[si]			; read two bytes
	cmp	ax,'\\'			; escaped backslash?
	jne	cnvln1a			; ne = no
	add	si,2			; formally read them
	jmp	short cnvln1b		; and write one copy to output
cnvln1a:call	katoi			; read char, convert ascii to binary
	cld
	stosb				; save the char
	inc	cx			; and count it
	or	ah,ah			; is returned number > 255?
	jz	cnvln1			; z = no, do more chars
cnvln1b:stosb				; save high order byte next
	inc	cx
	jmp	short cnvln1		; do more chars
cnvln2:	mov	byte ptr [di],0		; plant terminator
	clc				; clear c bit, success
	pop	es			; restore regs
	pop	di			; destination ptr
	pop	si			; source ptr
	pop	ax
	ret
cnvlin	endp

; Convert string by removing surrounding curly brace delimiter pair. 
; Converts text in place.
; Enter with source ptr in si.
; Preserves all registers, uses byte tmp. 9 Oct 1987 [jrd]
; 
cnvstr	proc	near
	push	ax
	push	cx
	push	dx
	push	si			; save start of source string
	push	di
	push	es
	mov	dx,si			; source address
	call	strlen			; get current length to cx
	jcxz	cnvst4			; z = nothing there
	push	ds
	pop	es			; set es to data segment
	cld
	mov	di,si			; set destination address to source
					; 2. Parse off curly brace delimiters
	cmp	byte ptr [si],braceop	; opening brace?
	jne	cnvst4			; ne = no, ignore brace-matching code
	inc	si			; skip opening brace
	mov	dl,braceop		; opening brace (we count them up)
	mov	dh,bracecl		; closing brace (we count them down)
	mov	tmp,1			; we are at brace level 1
cnvst1:	cld				; search forward
	lodsb				; read a string char
	stosb				; store char (skips opening brace)
	or	al,al			; at end of string?
	jz	cnvst4			; z = yes, we are done
	cmp	al,dl			; an opening brace?
	jne	cnvst2			; ne = no
	inc	tmp			; yes, increment brace level
	jmp	short cnvst1		;  and continue scanning

cnvst2:	cmp	al,dh			; closing brace?
	jne	cnvst1			; ne = no, continue scanning
	dec	tmp			; yes, decrement brace level
	cmp	byte ptr [si],0		; have we just read the last char?
	jne	cnvst3			; no, continue scanning
	mov	tmp,0			; yes, this is the closing brace
cnvst3:	cmp	tmp,0			; at level 0?
	jne	cnvst1			; ne = no, #opening > #closing braces
	mov	byte ptr [di-1],0	; plant terminator on closing brace

cnvst4:	pop	es			; recover original registers
	pop	di
	pop	si
	pop	dx
	pop	cx
	pop	ax
	ret
cnvstr	endp

; Convert ascii strings of the form "\{bnnn}" to a binary dword in dx:ax.
; The braces are optional but must occur in pairs. Numeric base indicator "b"
; is O or o or X or x or D or d or missing, for octal, hex, or decimal (def).
; Enter with si pointing at "\".
; Returns binary value in ax with carry clear and si to right of "}" or at
; terminating non-numeric char if successful; otherwise, a failure,
; return carry set with si = entry value + 1 and first read char in al.
katoi	proc	near
	cld
	xor	ah,ah			; clear high order field
	lodsb				; get first two chars
	push	temp
	push	temp1
	push	cx			; save working reg
	push	si			; save entry si+1
	push	bx
	push	ax			; save read char
	xor	cx,cx
	mov	temp,cx
	mov	temp1,cx
	or	al,al			; end of text?
	jz	katoi1a			; z = yes, exit failure
	cmp	al,'\'			; escape char?
	je	katoi1b			; e = yes
katoi1a:jmp	katoix			; common jump point to exit failure
katoi1b:lodsb				; get next char, maybe brace
	or	al,al			; premature end?
	jz	katoi1a			; z = yes, exit failure
	xor	bx,bx			; no conv yet, assume no opening brace
	cmp	al,braceop		; opening brace?
	jne	katoi2			; ne = no, have number or base
	mov	bl,bracecl		; remember a closing brace is needed
	lodsb				; get number base, if any
katoi2:	mov	nbase,10		; assume decimal numbers
	or	al,al			; premature end?
	jz	katoix			; z = yes, exit failure
	cmp	al,'a'			; lower case?
	jb	katoi3			; b = no
	cmp	al,'z'			; in range of lower case?
	ja	katoi3			; a = no
	and	al,5fh			; map to upper case
katoi3:	cmp	al,'O'			; octal?
	jne	katoi4			; ne = no
	mov	nbase,8			; set number base
	jmp	short katoi6
katoi4:	cmp	al,'X'			; hex?
	jne	katoi5			; ne = no
	mov	nbase,16
	jmp	short katoi6
katoi5:	cmp	al,'D'			; decimal?
	jne	katoi7			; ne = no base char, assume decimal
	mov	nbase,10
katoi6:	lodsb				; get a digit
katoi7:	or	al,al			; premature end?
	jz	katoi8a			; z = yes, use it as a normal end
	cmp	al,bl			; closing brace?
	je	katoi9			; e = yes
	call	cnvdig			; convert ascii to binary digit
	jc	katoi8			; c = cannot convert
	inc	bh			; say we did a successful conversion
	push	ax			; save this byte's value
	mov	ax,temp1
	mul	nbase			; high order
	mov	temp1,ax		; keep low order part of product
	mov	ax,temp
	mul	nbase			; low order
	add	temp1,dx		; high order carry
	mov	temp,ax			; low order
	pop	ax			; recover binary digit
	xor	ah,ah			; clear high order value
	add	temp,ax
	adc	temp1,0
	jc	katoix			; c = overflow error, exit
	jmp	short katoi6		; get more

katoi8:	or	bl,bl			; closing brace needed?
	jnz	katoix			; nz = yes, but not found
katoi8a:dec	si			; backup to reread terminator
katoi9:	or	bh,bh			; did we do any conversion?
	jz	katoix			; z = no, exit failure
	pop	ax			; throw away old saved ax
	pop	bx			; restore bx
	pop	ax			; throw away starting si, keep current
	pop	cx			; restore old cx
	mov	dx,temp1
	mov	ax,temp
	pop	temp1
	pop	temp
	clc				; clear carry for success
	ret
katoix:	pop	ax			; restore first read al
	pop	bx
	pop	si			; restore start value + 1
	pop	cx			; restore old cx
	pop	temp1
	pop	temp
	stc				; set carry for failure
	ret
katoi	endp

cnvdig	proc	near			; convert ascii code in al to binary
	push	cx			; return carry set if cannot
	push	es			; nbase has numeric base
	push	di
	push	ax
	cmp	al,'a'			; lower case?
	jb	cnvdig1			; b = no
	cmp	al,'f'			; highest hex digit
	ja	cnvdigx			; a = illegal symbol
	sub	al,'a'-'A'		; convert 'a' to 'f' to upper case
cnvdig1:mov	di,offset numset	; set of legal number symbols
	mov	cx,nbase		; number of legal symbols in this base
	cmp	cx,cx			; preset z flag
	push	ds
	pop	es			; point es at data segment
	cld				; scan forward
	repne	scasb			; find character in set
	jne	cnvdigx			; ne = not found
	inc	cx			; offset auto-dec of repne scasb above
	sub	cx,nbase		; counted off minus length
	neg	cx			; two's complement = final value
	pop	ax			; saved ax
	mov	ax,cx			; return binary in al
	clc				; c clear for success
	jmp	short cnvdixx		; exit
cnvdigx:stc				; c set for failure
	pop	ax
cnvdixx:pop	di
	pop	es
	pop	cx
	ret
cnvdig	endp	

decout	proc	near		; display decimal number in ax
	push	ax
	push	cx
	push	dx
	mov	cx,10		; set the numeric base
	call	valout		; convert and output value
	pop	dx
	pop	cx
	pop	ax
	ret
decout	endp

valout	proc	near		; output number in ax using base in cx
				; corrupts ax and dx
	xor	dx,dx		; clear high word of numerator
	div	cx		; (ax / cx), remainder = dx, quotient = ax
	push	dx		; save remainder for outputting later
	or	ax,ax		; any quotient left?
	jz	valout1		; z = no
	call	valout		; yes, recurse
valout1:pop	dx		; get remainder
	add	dl,'0'		; make digit printable
	cmp	dl,'9'		; above 9?
	jbe	valout2		; be = no
	add	dl,'A'-1-'9'	; use 'A'--'F' for values above 9
valout2:mov	ah,conout
	int	dos
	ret
valout	endp

; Convert input in buffer pointed to by SI to real number which is returned
; in DX:AX.  Enter with string size in AH.
; Return carry set on failure, carry clear on success.
ATOI	PROC	NEAR
	xor	bx,bx		; high order of this stays 0
	mov	tmp,bl		; no input yet
	mov	cl,ah		; number of chars of input
	xor	ch,ch		; size of string
	xor	ax,ax		; init sum
	xor	dx,dx		; high order part of sum
	cld
atoi0:	jcxz	atoi4		; Fail on no input
	mov	bl,[si]		; get an input char
	inc	si
	dec	cx		; count number remaining
	cmp	bl,' '		; leading space?
	je	atoi0		; e = yes, skip it
	cmp	bl,','		; comma separator?
	je	atoi0		; e = yes, skip it
	dec	si		; back up source pointer for reread below
	inc	cx		; and readjust byte counter
atoi1:	mov	bl,[si]		; get a new char
	inc	si
	cmp	bl,'9'		; check range for '0' to '9'
	ja	atoi2		; above '9'
	cmp	bl,'0'
	jb	atoi2		; below '0'
	sub	bl,'0'		; take away ascii bias
	push	bx		; save digit and high null
	shl	ax,1
	rcl	dx,1		; current sum * 2
	push	dx
	push	ax		; save it
	shl	ax,1
	rcl	dx,1
	shl	ax,1
	rcl	dx,1		; times * 4 more
	pop	bx		; low * 2
	add	bx,ax
	mov	ax,bx		; low order * 10
	pop	bx
	adc	bx,dx
	mov	dx,bx		; high order * 10
	pop	bx		; new digit and high null
	add	ax,bx		; plus new digit
	adc	dx,0
	mov	tmp,1		; say have sum being computed
	loop	atoi1
	inc	si		; inc for dec below
atoi2:	dec	si		; point at terminator
	cmp	tmp,0		; were any digits discovered?
	je	atoi4		; e = no, fail
	clc			; success
	ret
atoi4:	mov	dx,offset erms25 ; Input must be numeric
	stc			; failure
	ret
ATOI	ENDP

; Write binary number in AX as decimal asciiz to buffer pointer DI.
dec2di	proc	near		; output number in ax using base in cx
	push	ax
	push	cx
	push	dx
	mov	cx,10
	call	dec2di1		; recursive worker
	pop	dx
	pop	cx
	pop	ax
	ret

dec2di1:xor	dx,dx		; clear high word of numerator
	div	cx		; (ax / cx), remainder = dx, quotient = ax
	push	dx		; save remainder for outputting later
	or	ax,ax		; any quotient left?
	jz	dec2di2		; z = no
	call	dec2di1		; yes, recurse
dec2di2:pop	dx		; get remainder
	add	dl,'0'		; make digit printable
	mov	[di],dl		; store char in buffer
	inc	di
	mov	byte ptr[di],0	; add terminator
	ret
dec2di	endp


; OPEN { READ | WRITE | APPEND } filespec
vfopen	proc	near
	mov	ah,cmkey		; get READ/WRITE/APPEND keyword
	mov	dx,offset opntab	; keyword table
	xor	bx,bx			; help
	call	comnd
	jc	vfopen1			; c = failed
	mov	temp,bx
	mov	ah,cmword		; read filespec
	mov	dx,offset rdbuf		; buffer for filename
	mov	bx,offset vfophlp	; help
	call	comnd
	jc	vfopen1			; c = failed
	mov	ah,cmeol		; get end of line confirmation
	call	comnd
	jnc	vfopen2
	mov	kstatus,ksgen		; general cmd failure status
vfopen1:ret				; error return, carry set

vfopen2:mov	kstatus,kssuc		; assume success status
	mov	dx,offset rdbuf		; filename, asiiz
	mov	bx,temp			; kind of open
	cmp	bx,1			; open for reading?
	jne	vfopen4			; ne = no
					; OPEN READ
	cmp	vfrhandle,0		; is it open now?
	jge	vfopen8			; ge = yes, complain
	mov	ah,open2		; file open
	xor	al,al			; 0 = open readonly
	cmp	dosnum,300h		; at or above DOS 3?
	jb	vfopen3			; b = no, so no shared access
	or	al,40h			; open readonly, deny none
vfopen3:int	dos
	jc	vfopen9			; c = failed to open the file
	mov	vfrhandle,ax		; save file handle
	clc
	ret
					; OPEN WRITE or APPEND
vfopen4:cmp	vfwhandle,0		; is it open now?
	jge	vfopen8			; ge = yes, complain
	mov	ax,dx			; filename for isfile
	call	isfile		; check for read-only/system/vol-label/dir
	jc	vfopen7			; c = file does not exist
	test	byte ptr filtst.dta+21,1fh	; the no-no file attributes
	jnz	vfopen9			; nz = do not write over one of these
vfopen5:test	filtst.fstat,80h	; access problem?
	jnz	vfopen9			; nz = yes, quit here
	test	byte ptr filtst.dta+21,1bh	; r/o, hidden, volume label?
	jnz	vfopen9			; we won't touch these
	mov	ah,open2	       ; open existing file (usually a device)
	mov	al,1+1			; open for writing
	int	dos
	jc	vfopen9			; carry set means can't open
	mov	vfwhandle,ax		; remember file handle
	mov	bx,ax			; file handle for lseek
	xor	cx,cx
	xor	dx,dx			; cx:dx = displacment
	xor	al,al			; 0 means from start of file
	cmp	temp,2			; WRITE? means from start of file
	je	vfopen6			; e = yes, else APPEND
	mov	al,2			; move to eof
vfopen6:mov	ah,lseek		; seek the place
	int	dos
	clc
	ret

vfopen7:cmp	temp,1			; READ?
	je	vfopen9			; e = yes, should not be here
	mov	ah,creat2		; create file
	xor	cx,cx			; 0 = attributes bits
	int	dos
	jc	vfopen9			; c = failed
	mov	vfwhandle,ax		; save file handle
	clc				; carry clear for success
	ret

vfopen8:mov	dx,offset vfoptwice	; trying to reopen a file
	mov	ah,prstr
	int	dos
	mov	kstatus,kstake		; Take file failure status
	stc
	ret
vfopen9:mov	dx,offset vfopbad	; can't open, complain
	mov	ah,prstr
	int	dos
	mov	dx,offset rdbuf		; filename
	call	prtasz
	mov	kstatus,kstake		; Take file failure status
	stc
	ret
vfopen	endp

; CLOSE {READ-FILE | WRITE-FILE}
vfclose	proc	near
	mov	tmp,al			; remember kind (1=READ, 2=WRITE)
	mov	bx,vfrhandle		; READ FILE handle
	cmp	al,1			; READ-FILE?
	je	vfclos1			; e = yes
	cmp	al,2			; WRITE-FILE?
	jne	vfclos3			; ne = no, a mistake
	mov	bx,vfwhandle		; write handle
	or	bx,bx
	jl	vfclos3			; l = invalid handle
	mov	dx,offset rdbuf
	xor	cx,cx			; zero length
	push	bx			; save handle
	mov	ah,write2		; set file high water mark
	int	dos
	pop	bx
vfclos1:or	bx,bx
	jl	vfclos3			; l = invalid handle
	mov	kstatus,kssuc		; success status thus far
	mov	ah,close2		; close file
	int	dos
	jc	vfclos4			; c = error
	cmp	tmp,1			; READ?
	jne	vfclos2			; ne = no, WRITE
	mov	vfrhandle,-1		; declare READ handle to be invalid
	clc
	ret
vfclos2:mov	vfwhandle,-1		; declare WRITE handle to be invalid
	clc
	ret
vfclos4:mov	ah,prstr
	mov	dx,offset vfclbad	; complain
	int	dos
vfclos3:mov	kstatus,ksgen		; general cmd failure status
	stc
	ret
vfclose	endp

; READ-FILE variable name
vfread	proc	near
	mov	comand.cmper,1		; do not react to '\%' in macro name
	mov	ah,cmword
	mov	dx,offset rdbuf+2	; buffer for macro name
	mov	word ptr rdbuf,0
	mov	bx,offset vfrdmsg
	call	comnd			; get macro name
	jnc	vfread1			; nc = success
	mov	kstatus,ksgen		; general cmd failure status
	ret				; failure
vfread1:or	ax,ax			; null entry?
	jnz	vfread2			; nz = no
	mov	dx,offset vfrdbad	; more parameters needed
	mov	ah,prstr
	int	dos
	mov	kstatus,ksgen		; general cmd failure status
	stc
	ret

vfread2:push	ax
	mov	ah,cmeol		; get command confirmation
	call	comnd
	pop	ax			; ax is variable length
	jc	vfreadx			; c = failed
	mov	cx,cmdblen		; length of rdbuf
	sub	cx,ax			; minus macro name
	dec	cx			; minus space separator
	mov	temp,0			; leading whitespace and comment flgs
	mov	di,offset rdbuf+2	; destination buffer
	add	di,ax			; plus variable name
	mov	byte ptr [di],' '	; space separator
	inc	di			; put definition here
	mov	bx,vfrhandle		; READ FILE handle
	or	bx,bx			; check for valid handle
	jge	vfread3			; ge = valid
	mov	ah,prstr
	mov	dx,offset vfnofile	; say no file
	int	dos
vfreadx:mov	kstatus,ksgen		; general cmd failure status
	stc				; failure return
	ret
vfread3:push	cx			; read from file
	mov	kstatus,kssuc		; assume success status
	mov	cx,1			; read 1 char
	mov	dx,di			; place here
	mov	byte ptr [di],0		; insert terminator
	mov	ah,readf2
	int	dos
	pop	cx
	jc	vfreadx			; c = read failure
	or	ax,ax			; count of bytes read
	jz	vfread9			; z means end of file
	mov	al,[di]			; get the character
	cmp	flags.takflg,0		; echo Take files?
	je	vfread3a		; e = no
	push	ax
	mov	ah,conout
	mov	dl,al			; echo character
	int	dos
	pop	ax
vfread3a:cmp	al,CR			; CR?
	je	vfread7			; e = yes, ignore it
	cmp	al,LF			; LF?
	je	vfread8			; e = yes, exit
	cmp	byte ptr temp,0		; seen non-spacing char yet?
	jne	vfread6			; ne = yes
	cmp	al,' '			; is this a space?
	je	vfread7			; e = yes, skip it
	cmp	al,TAB			; or a tab?
	je	vfread7			; e = yes, skip it
	mov	byte ptr temp,1		; say have seen non-spacing char

vfread6:inc	di			; next storage cell
vfread7:loop	vfread3			; loop til end of line

vfread8:cmp	byte ptr [di-1],'-'	; last printable is line-continuation?
	jne	vfread10		; ne = no, use this line as-is
	dec	di			; point to hyphen
	mov	cx,offset rdbuf+cmdblen	; end of rdbuf
	sub	cx,di			; minus where we are now
	jle	vfread10		; le = no space remaining
	mov	temp,0			; leading whitespace and comment flgs
	jmp	vfread3			; read another line

vfread9:mov	kstatus,ksgen		; EOF, general command failure status

vfread10:cmp	flags.takflg,0		; echo Take files?
	je	vfread11		; e = no
	mov	ah,prstr
	mov	dx,offset crlf		; for last displayed line
	int	dos

vfread11:mov	byte ptr [di],0		; insert final terminator
	mov	dx,offset rdbuf+2	; start counting from here
	push	kstatus			; preserve status from above work
	call	strlen			; cx becomes length
	call	dodecom			; create the variable
	pop	kstatus			; recover reading status
	jc	vfread12		; c = failure
	ret
vfread12:mov	kstatus,ksgen		; general command failure status
	mov	ah,prstr
	mov	dx,offset vfrbad	; say error while reading
	int	dos
	stc
	ret
vfread	endp

; WRITE {FILE or log} text
Write	proc	near
	mov	ah,cmkey		; get kind of log file
	mov	dx,offset writetab	; table of possibilities
	xor	bx,bx			; help, when we get there
	call	comnd
	jnc	write1			; nc = success
	mov	kstatus,ksgen		; general cmd failure status
	ret
write1:	mov	temp,bx			; save log file kind
	mov	ah,cmline		; get string to be written
	mov	bx,offset rdbuf		; where to put text
	mov	dx,offset msgtxt	; help
	call	comnd
	mov	ah,cmeol
	call	comnd
	jnc	wpkt0
	mov	kstatus,ksgen		; general cmd failure status
	ret				; c = failure
wpkt0:	mov	kstatus,kssuc		; success status thus far
	mov	si,offset rdbuf		; start of text in buffer
	mov	di,si			; convert in-place, to asciiz
	call	cnvlin
	mov	bx,temp			; log file kind
	mov	si,offset rdbuf
	mov	dx,si
	call	strlen
	jcxz	wpkt2			; z = no chars to write
	cmp	bx,logpkt		; Packet log?
	jne	wses1			; ne = no
wpkt1:	lodsb				; get byte to al
	call	pktcpt			; write to packet log
	loop	wpkt1
wpkt2:	clc				; say success
	ret

wses1:	cmp	bx,logses		; Session log?
	jne	wtrn1			; ne = no
wses2:	lodsb				; get byte to al
	call	cptchr			; write to log
	loop	wses2			; do cx chars
	clc				; say success
	ret

wtrn1:	cmp	bx,logtrn		; Transaction log?
	jne	wtrn2			; ne = no
	mov	bx,tloghnd		; file handle
	jmp	short wtrn4

wtrn2:	cmp	bx,80h			; WRITE SCREEN?
	jne	wtrn3			; ne = no
	xor	bx,bx			; handle is stdout
	jmp	short wtrn4

wtrn3:	cmp	bx,4002h		; WRITE FILE?
	jne	wtrn5			; ne = no, forget this
	mov	bx,vfwhandle		; handle

wtrn4:	or	bx,bx			; is handle valid?
	jl	wtrn5			; l = no
	mov	ah,write2		; write to file handle in bx
	int	dos
	jc	wtrn4a			; c = failure
	ret
wtrn4a:	mov	ah,prstr
	mov	dx,offset vfwbad	; say error while writing
	int	dos
	mov	kstatus,ksgen
	stc
	ret
wtrn5:	mov	kstatus,ksgen		; general cmd failure status
	mov	ah,prstr
	mov	dx,offset vfnofile	; say no file
	int	dos
	stc
	ret
write	endp
code	ends 
	end
