   	NAME	msscmd
; File MSSCMD.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
; 13 March 1991 version 3.10
; 14 Nov 1993 Add variables in prompt string
; 5 Jan 1991 Add \$(Environment variable) substitution.
; 9 Sept 1990 Add \v(named variable) substitution.
; 21 Nov 1988 Version 2.32
; 17 Oct 1988 Make command keyword search failure yield global kstatus of
;  failure status, to inform Take/Macros of a defective command.
; 4 Aug 1988 Fix keyword count initialization in help display.
; 1 July 1988 Version 2.31
; 7 June 1988 Add comand.impdo flag to permit a keyword search failure to
;  retry as a DO cmd (macro table). Used only by main Kermit command level.
; 27 May 1988 Allow "-<cr>" as line continuation pair and let "\-<cr>" 
;  stand for "-<end of line>". Add comand.cmblen to sense buffer overflow on
;  calls to cmtxt; if comand.cmblen is left at zero then use 128 byte limit
;  (comand.cmblen is cleared at command end).
; 15 May 1988 Make :label keywords no-ops, make query ordinary char in TAKEs.
; 6 May 1988 Show ambiguous keywords at parse time, do graceful interaction.
; 28 March 1988 Permit '\%x' (x = '0' or above) in commands as 'variables'
;  (substituted string of text). Words are obtained from Macro table mcctab.
;  DEF MAC and DO MAC define variables. DEF macro uses comand.cmper > 0 to
;  allow '\%x'to be stored as a literal. Variables work in any command except
;  DEF MAC. Major redesign of whole parser. [jrd]
; 4 March 1988 Rewrite keyword parsing to permit non-alphabetized tables
;  and 8-bit characters. Add byte comand.cmwhite which when non-zero permits
;  leading whitespace in cmline and cmword commands; it is reset at command
;  completion. Move procedure Prompt here from mssker. [jrd]
; 27 Feb 1988 Add capability of stdin being a file. [jrd]
; 1 Jan 1988 version 2.30

 	public comnd, comand, isdev, iseof, prompt, tolowr, valtab
	public parstate, pardone, parfail, nparam, param, lparam, ninter
	public inter, atparse, atpclr, atdispat, cspb, dspb, mprompt, nvaltoa
	public savepoff, saveplen, keyboard, fwrtdir, cspb1, filetdate
	public	fqryenv

env	equ	2CH			; environment address in psp
braceop	equ	7bh			; opening curly brace
bracecl	equ	7dh			; closing curly brace

; codes for various \fxxx(arg list) operations
F_char		equ	1
F_code		equ	2
F_contents 	equ	3
F_definition	equ	4
F_eval		equ	5
F_exec		equ	6
F_files		equ	7
F_index		equ	8
F_length	equ	9
F_literal	equ	10
F_lower		equ	11
F_lpad		equ	12
F_maximum 	equ	13
F_minimum 	equ	14
F_nextfile	equ	15
F_repeat 	equ	16
F_reverse	equ	17
F_right		equ	18
F_rpad		equ	19
F_substr	equ	20
F_upper		equ	21
F_date		equ	22
F_size		equ	23
F_replace	equ	24

; Constants for valtoa 
V_environ	equ	0
V_argc		equ	1
V_carrier	equ	22
V_charset	equ	33
V_console	equ	27
V_count		equ	2
V_cmdlevel	equ	29
V_cps		equ	24
V_date		equ	3
V_ndate		equ	14
V_dir		equ	5
V_dosver	equ	19
V_errlev	equ	4
V_kbd		equ	10
V_line		equ	15
V_monitor 	equ	28
V_parity 	equ	21
V_platform 	equ	8
V_port 		equ	15
V_program 	equ	12
V_prompt 	equ	23
V_query		equ	31
V_session 	equ	17
V_tcpip		equ	20
V_space 	equ	25
V_speed 	equ	11
V_startup 	equ	26
V_status 	equ	13
V_sysid		equ	32
V_system 	equ	9
V_terminal 	equ	16
V_time		equ	6
V_ntime 	equ	18
V_version 	equ	7
V_input		equ	30
V_inpath	equ	34
V_disk		equ	35
V_cmdfile	equ	36
V_inidir	equ	37

; Like main filest, but shorter for local usage.
fileiost struc
dta	db 26 dup(0)	; DOS, 21 resev'd bytes, file attr, 2 each date & time
sizelo	dw 0		; DOS, file size double word
sizehi	dw 0
fname	db 13 dup(0)	; DOS, filename, asciiz, with dot. End of DOS section
fileiost ends

data 	segment
	extrn	flags:byte, taklev:byte, takadr:word, mcctab:byte
	extrn	kstatus:word, oldtak:byte, errlev:byte, psp:word
	extrn	portval:word, bdtab:byte, tdfmt:byte, machnam:byte
	extrn	verident:word, kstatus:word, errlev:byte, comptab:byte
	extrn	termtb:byte, dosnum:word, prmptr:word, startup:byte
	extrn	crt_mode:byte, tmpbuf:byte, buff:byte, input_read:word
	extrn	decbuf:byte, encbuf:byte, queryseg:word, inpath_seg:word
	extrn	cmdfile:byte, inidir:byte, dos_bottom:byte
ifndef	no_tcp
	extrn	sescur:word, tcp_status:word
endif	; no_tcp

				; Start Patch structure. Must be first.
	even
dspb	dw	code		; segment values for patcher
	dw	code1
	dw	code2
	dw	data
	dw	data1
	dw	_TEXT
	db	64 dup (0)	; data segment patch buffer
				;  with space for other material, if req'd
				; end of Patch structure
progm	db	'MS-DOS_KERMIT$'	; for \v(program)
system	db	'MS-DOS$'		; for \v(system)
keyboard dw	88			; \v(keyboard) kind of keybd 88/101

comand	cmdinfo	<>
cmer00  db      cr,lf,'?Program internal error, recovering$'
cmer01	db	cr,lf,'?More parameters are needed$'
cmer02	db	cr,lf,'?Word "$'
cmer03	db	'" is not usable here$'
cmer04	db	'" is ambiguous$'
cmer07	db	cr,lf,'?Ignoring extra characters "$'
cmer08	db	'"$'
cmer09	db	cr,lf,'?Text exceeded available buffer capacity$'
cmin01	db	' Use one of the following words in this position:',cr,lf,'$'
stkmsg	db	cr,lf,bell,'?Exhausted work space! Circular definition?$'
moremsg	db	'... more, press a key to continue ...$'
crlf    db      cr,lf,'$'
ctcmsg	db	5eh,'C$'
cmunk	db	'unknown'
errflag	db	0			; non-zero to suppress cmcfrm errors
kwstat	db	0			; get-keyword status
subcnt	db	0			; count of chars matched in '\%'
subtype db	0			; kind of sub (% or v)
evaltype dw	0			; evaltoa kind
bracecnt db	0			; curly brace counter
argptr	dw	3 dup (0)		; \fxxx() argument pointers
arglen	dw	3 dup (0)		; \fxxx() argument lengths
replay_cnt dw	0			; report to app, no echo
fileio	fileiost <>			; find first/next support
cmsflg	db	0			; Non-zero when last char was a space
in_showprompt db 0			; Non-zero when in showprompt
cmdbuf	db	cmdblen dup (0)		; Buffer for command parsing
rawbuf	db	cmdblen dup (0)
read_source db	0
endedbackslash db 0
in_reparse db	0

	even
cmdstk	dw	0			; stack pointer at comand call time
cmptab	dw	0			; Address of present keyword table
cmhlp	dw	0			; Address of present help
cmwptr	dw	0			; Pointer for next char write
cmrptr	dw	0			; Pointer for next char read
cmsiz	dw	0			; Size info of user input
cmsptr	dw	0			; Place to save a pointer
mcmprmp	dw	0			; master prompt, string address
mcmrprs	dd	0			; master prompt, reparse address
mcmostp	dw	0			; master prompt, stack pointer
temp	dw	0			; temp (counts char/line so far)
cmrawptr dw	0			; non-substitution level, write ptr

ifndef	no_tcp
valtab	db	1+37			; table of values for \v(value)
else
valtab	db	1+37 - 2
endif	; no_tcp
	mkeyw	'argc)',V_argc
	mkeyw	'carrier)',V_carrier
	mkeyw	'charset)',V_charset
	mkeyw	'cmdfile)',V_cmdfile
	mkeyw	'console)',V_console
	mkeyw	'count)',V_count
	mkeyw	'cmdlevel)',V_cmdlevel
	mkeyw	'cps)',V_cps
	mkeyw	'date)',V_date
	mkeyw	'ndate)',V_ndate
	mkeyw	'directory)',V_dir
	mkeyw	'dosversion)',V_dosver
	mkeyw	'disk)',V_disk
	mkeyw	'errorlevel)',V_errlev
	mkeyw	'inidir)',V_inidir
	mkeyw	'inpath)',V_inpath
	mkeyw	'input)',V_input
	mkeyw	'keyboard)',V_kbd
	mkeyw	'line)',V_port
	mkeyw	'monitor)',V_monitor
	mkeyw	'parity)',V_parity
	mkeyw	'platform)',V_platform
	mkeyw	'port)',V_port
	mkeyw	'program)',V_program
	mkeyw	'prompt)',V_prompt
	mkeyw	'query)',V_query
ifndef	no_tcp
	mkeyw	'session)',V_session
	mkeyw	'tcpip_status)',V_tcpip
endif	; no_tcp
	mkeyw	'space)',V_space
	mkeyw	'speed)',V_speed
	mkeyw	'startup)',V_startup
	mkeyw	'status)',V_status
	mkeyw	'sysid)',V_sysid
	mkeyw	'system)',V_system
	mkeyw	'terminal)',V_terminal
	mkeyw	'time)',V_time
	mkeyw	'ntime)',V_ntime
	mkeyw	'version)',V_version


evaltab	db	22			; \fverb(args)
	mkeyw	'character(',F_char
	mkeyw	'code(',F_code
	mkeyw	'contents(',F_contents
	mkeyw	'date(',F_date
	mkeyw	'definition(',F_definition
	mkeyw	'files(',F_files
	mkeyw	'index(',F_index
	mkeyw	'length(',F_length
	mkeyw	'literal(',F_literal
	mkeyw	'lower(',F_lower
	mkeyw	'lpad(',F_lpad
	mkeyw	'maximum(',F_maximum
	mkeyw	'minimum(',F_minimum
	mkeyw	'nextfile(',F_nextfile
	mkeyw	'repeat(',F_repeat
	mkeyw	'replace(',F_replace
	mkeyw	'reverse(',F_reverse
	mkeyw	'right(',F_right
	mkeyw	'rpad(',F_rpad
	mkeyw	'size(',F_size
	mkeyw	'substr(',F_substr
	mkeyw	'upper(',F_upper
; not implemented in MS-DOS Kermit
	mkeyw	'evaluate(',F_eval
	mkeyw	'execute(',F_exec

envtab	db	1			     	; \$(..) Environment table
	mkeyw	'An Environment variable)',0	; reserve 0 in valtab

						; table of Parity strings
parmsgtab dw	parevnmsg,parmrkmsg,parnonmsg,paroddmsg,parspcmsg
parevnmsg db	'EVEN',0
parmrkmsg db	'MARK',0
parnonmsg db	'NONE',0
paroddmsg db	'ODD',0
parspcmsg db	'SPACE',0

onoffmsgtab dw	offmsg,onmsg		; on/off table, asciiz
offmsg	db	'OFF',0
onmsg	db	'ON',0

ansiword db	'ANSI',0		; \v(console) ASCIIZ strings
noneword db	'NONE',0
colormsg db	'COLOR',0		; \v(monitor) ASCIIZ strings
monomsg	db	'MONO',0

valtmp	dw	0

	even
envadr	dd	0			; seg:offset of a string in Environemt
envlen	dw	0			; length of envadr's string

	even				; Control sequence storage area
maxparam equ	16			; number of ESC and DCS Parameters
maxinter equ	16			; number of ESC and DCS Intermediates

savepoff label	word			; Start per session save area
parstate dw	0			; parser state, init to startup
pardone dw	0			; where to jmp after Final char seen
parfail	dw	0			; where to jmp if parser fails
nparam	dw	0			; number of received Parameters
param	dw	maxparam dup (0)	; Parameters for ESC
lparam	db	0			; a single letter Parameter for ESC
ninter	dw	0			; number of received Intermediates
inter	db	maxinter dup (0),0	; Intermediates for ESC, + guard 
saveplen dw	($-savepoff)
data	ends

data1	segment
	db	0			; so error msg has non-zero offset
cmin00  db      ' Press ENTER to execute command$'
f1hlp	db	' number$'
f2hlp	db	' char$'
f3hlp	db	' variable or macro name$'
f8hlp	db	' pattern-text, string, start-position$'
f9hlp	db	' text$'
f12hlp	db	' text, pad-length, pad-char$'
f15hlp	db	' no argument$'
f16hlp	db	' repeat-text, repeat-count$'
f18hlp	db	' text, right-most count$'
f20hlp	db	' text, start-position, substring len$'
f22hlp	db	' filename$'
data1	ends

ifndef	no_terminal
ifndef	no_tcp
_TEXT	segment
	extrn	cpatch:far
_TEXT	ends
endif	; no_tcp
endif	; no_terminal

code1	segment
	extrn	shovarcps:near

cspb1	equ	this byte
	db	(256-($-cspb1)) dup (0)	; code1 segment patch buffer
				; end of Patch area
code1	ends

code	segment
	extrn	ctlu:near, cmblnk:near, locate:near, takrd:near, dec2di:near
	extrn	takclos:near, docom:near, prtasz:near, getenv:near
	extrn	getbaud:near, strlen:near, prtscr:near, strcpy:near
	extrn	dskspace:near, lnout:near, katoi:near, takopen_sub:far
	extrn	malloc:far, buflog:far, takopen_macro:far
extrn decout:near
	assume	cs:code, ds:data, es:nothing

				; Patch area. Must be first in MSK's Code Seg
cspb	equ	this byte
	dw	seg code1
	dw	seg code2
	dw	seg data
	dw	seg data1
	dw	seg _TEXT
	dw	seg _DATA
ifndef	no_terminal
ifndef	no_tcp
	dw	offset cpatch
endif	; no_tcp
endif	; no_terminal
	db	(256-($-cspb)) dup (0)	; code segment patch buffer
				; end of Patch area

fctlu	proc	far		; FAR callable versions of items in seg code
	call	ctlu		;  for calling from code segment code1 below
	ret
fctlu	endp
fcmblnk	proc	far
	call	cmblnk
	ret
fcmblnk	endp
fgetbaud proc	far
	call	getbaud
	ret
fgetbaud endp
fgetenv	proc	far
	call	getenv
	ret
fgetenv	endp
flocate	proc	far
	call	locate
	ret
flocate	endp
ftakrd	proc	far
	call	takrd
	ret
ftakrd	endp
fkatoi	proc	far
	call	katoi
	ret
fkatoi	endp

fdec2di	proc	far
	push	cx
	push	ds
	mov	cx,es
	mov	ds,cx
	call	dec2di
	pop	ds
	pop	cx
	ret
fdec2di	endp
fstrlen	proc	far
	call	strlen
	ret
fstrlen	endp
ftakclos proc	far
	call	takclos
	ret
ftakclos endp
fprtasz	proc	far
	call	prtasz
	ret
fprtasz	endp
fprtscr	proc	far
	call	prtscr
	ret
fprtscr	endp
fisdev	proc	far
	call	isdev
	ret
fisdev	endp
fiseof	proc	far
	call	iseof
	ret
fiseof	endp
ftolowr	proc	far
	call	tolowr
	ret
ftolowr	endp
nvaltoa	proc	near
	push	es
	mov	ax,ds
	mov	es,ax
	call	fvaltoa
	pop	es
	ret
nvaltoa	endp
flnout	proc	far
	call	lnout
	ret
flnout	endp
fdskspace proc	far
	call	dskspace
	ret
fdskspace endp
fstrcpy	proc	far
	call	strcpy
	ret
fstrcpy	endp
FDECOUT	proc	far
	call decout
	ret
fdecout	endp

;       This routine parses the specified function in AH. Any additional
;       information is in DX and BX.
;       Returns carry clear on success and carry set on failure
 
COMND	PROC NEAR
	mov	cmdstk,sp		; save stack ptr for longjmp exit
	mov	bracecnt,0		; curly brace counter
	mov	al,taklev		; Take level now
	mov	oldtak,al		; remember past internal takclos call
	cmp	ah,cmeol		; Parse a confirm?
	jne	cm2			; nz = no
	call	cmcfrm			; get a Carriage Return end of line
	ret
cm2: 	cmp	ah,cmkey		; Parse a keyword?
	jne	cm3
	call	cmkeyw			; try and get one
	ret
cm3:	cmp	ah,cmline		; parse line of text
	jne	cm4
	call	cmtext
	ret
cm4:	cmp	ah,cmword		; parse arbitrary word
	jne	cm5
	call	cmfil0
	ret
cm5:	mov	ah,prstr		; else give error
	mov	dx,offset cmer00	; "?Program internal error"
	int	dos
	jmp	prserr			; reparse
					; Control-C exit path (far to near)
cmexit	label	far
	mov	sp,cmdstk		; restore command entry stack pointer
	stc
	retn				;  and fail immediately (a longjmp)

COMND	ENDP
code	ends

code1	segment
	assume	cs:code1

; This routine parses a keyword from the table pointed at by DX, help text
; point to by BX. Format of the table is as follows (use macro mkeyw):
;	addr:	db	N	  ; Where N is the # of entries in the table
;		dw	M	  ; M is the size of the keyword
;		db	'string'  ; String is the keyword
;		dw	value	  ; Value is data to be returned
; Keywords may be in any order and in mixed case.
; Return is rskp for success and ret for failure.

; cmptab: pointer to keyword table (supplied by caller)
; cmhlp: pointer to help message (supplied by caller)
; cmsptr: pointer to current user word text
; cmsiz: length of user text, excluding terminator
; comand.cmcr: 0 = empty lines not allowed, 1 = empty lines allowed
; comand.cmwhite: non-zero allows leading whitespace for cmline and cmword,
;                 reset automatically at end of call
; cmwptr: buffer write pointer to next free byte
; cmrptr: buffer read pointer for next free byte
; comand.cmper:	0 to do \%x substitution. Set to 0 at end of call
; comand.impdo: non-zero permits keyword failure to retry as DO command, reset
;		 automatically at time of failure.
cmkeyw	proc	far
	mov	cmsiz,0			; user word length
	mov	ax,cmrptr		; get command reading pointer
	mov	cmsptr,ax		; set pointer for start of user word
	mov	cmhlp,bx		; save the help pointer
        mov	cmptab,dx		; save the beginning of keyword table
	mov	bx,dx
	cmp	byte ptr[bx],0		; get number of entries in table
	jne	cmky1
	jmp	cmky7			; e = no keywords to check, error
cmky1:	mov	cmsflg,0ffh		; skip leading spaces/tabs
	call	cmgtch			; get char from the user into ah
	jc	cmky3			; c = terminator
	mov	dx,cmrptr		; next byte to read
	dec	dx			; where we just read a char
	mov	cmsptr,dx		; remember start of keyword
	inc	cmsiz			; start counting user chars
cmky2:	call	cmgtch			; read until terminator
	jc	cmky3			; c = terminator
	inc	cmsiz			; count user chars
	jmp	short cmky2		; no terminator yet

cmky3:	cmp	ah,'?'              	; need help?
	jne	cmky4			; ne = no
	call	cmkyhlp			; display help
	jmp	repars
cmky4:	cmp	ah,escape		; escape?
	jne	cmky6			; ne = no
	call	cmkyesc			; process escape
	jc	cmky5			; c = failure (no unique keyword yet)
	mov	comand.cmper,0		; reset to variable recognition
	mov	comand.cmkeep,0
	mov	comand.impdo,0		; clear flag to prevent loops
	mov	comand.cmquiet,0	; permit echoing again
	clc
	ret				; return successfully to user

cmky5:	cmp	cmsiz,0			; started a word yet?
	je	cmky1			; e = no, ignore escape, keep looking
	call	cmkyhlp			; display help
	jmp	repars

cmky6:	cmp	cmsiz,0			; length of user's text, empty?
	je	cmky7			; e = yes, parse error
	push	bx
	mov	bx,cmsptr		; point at first user character
	cmp	byte ptr[bx],':'	; start of a label?
	pop	bx
	jne	cmky6a			; ne = no, return success
	mov	cmsiz,1			; say just one byte
cmky6a:	call	getkw			; get unique kw, point to it with bx
	jc	cmky8			; c = not found
	add	bx,[bx]			; add length of keyword text (CNT)
	add	bx,2			; point at value field
	mov	bx,[bx]			; bx = return value following keyword
	xor	ax,ax
	mov	comand.cmper,al		; reset to variable recognition
	mov	comand.cmkeep,al
	mov	comand.impdo,al		; clear flag to prevent loops
	mov	comand.cmquiet,al	; permit echoing again
	mov	errflag,al
	clc
	ret				; return successfully
					; all other terminators come here
cmky7:	cmp	cmsiz,0			; empty table or empty user's text?
	jne	cmky8			; ne = no
	cmp	comand.cmcr,0		; empty lines allowed?
	jne	cmky10			; ne = yes, do not complain
	push	dx
	mov	ah,prstr
	mov	dx,offset cmer01	; command word expected
	int	dos
	pop	dx
	xor	al,al
	mov	comand.cmquiet,al	; permit echoing again
	mov	comand.impdo,al		; clear flag to prevent loops
	stc				; failure
	ret

cmky8:	cmp	comand.impdo,0		; failed here, ok to try Macro table?
	je	cmky8a			; e = no, use regular exit path
	mov	comand.impdo,0		; yes, but clear flag to prevent loops
	mov	cmrptr,offset cmdbuf	; reinit read pointer
	mov	comand.cmquiet,1	; suppress echoing of same keyword
	mov	bx,offset docom		; return DO as "found" keyword
	clc
	ret				; return success to invoke DO

cmky8a:	mov	errflag,1		; say already doing error recovery
	or	kstatus,ksgen		; global command status, failure
	mov	comand.cmquiet,0	; permit echoing again
	call	fisdev			; reading pretyped lines?
	jnc	cmky9			; nc = yes, consume rest of line
	cmp	taklev,0		; in a Take file?
	jne	cmky9			; ne = yes
	call	cmskw			; display offending keyword
	mov	comand.impdo,0		; clear flag to prevent loops
	dec	cmrptr			; interactive, backup to terminator
	mov	bx,cmrptr		; look at it
	cmp	byte ptr [bx],' '	; got here on space terminator?
	jne	cmky10			; ne = no, (cr,lf,ff) exit failure
	mov	ah,prstr		; start a fresh line
	mov	dx,offset crlf
	int	dos
	call	bufdel			; cut back buffer to last good char
	jmp	repars			; reparse interactive lines

cmky9:	call	cmcfrm			; get formal end of command line
					;  to maintain illusion of typeahead
					;  and let user backspace to correct
					;  mistakes (we reparse everything)
	call	cmskw			; display offending keyword
cmky10:	mov	comand.cmquiet,0	; permit echoing again
	stc				; say failure
	ret
cmkeyw	endp

;;;;;; start support routines for keyword parsing.

cmkyesc	proc	near			; deal with escape terminator
	push	cmrptr			; points at ESC
	pop	cmwptr			; pointed one byte beyond ESC
	cmp	cmsiz,0 		; user word length, empty?
	jne	cmkye2			; ne = have user text, else complain
cmkye1:	call	esceoc			; do normal escape end-of-command
	stc				; say failure to fill out word
	ret
					; add unique keyword to buffer
cmkye2:	call	getkw			; is there a matching keyword?
	jc	cmkye1			; c = ambiguous or not found
	push	bx			; unique, bx points to structure
	push	si
	mov	cx,[bx]			; length of keyword
	add	bx,2			; point to first letter
	dec	cmrawptr		; overwrite ESC
	mov	si,cmrawptr		; where raw writes go
	mov	dx,cmsiz		; length of user word
	add	bx,dx			; add chars known so far
	sub	cx,dx			; calculate number yet to add
	jcxz	cmkye4			; z = none
cmkye3:	mov	al,[bx]			; get a keyword letter
	inc	bx
	call	ftolowr			; lowercase
	mov	[si],al			; store it
	inc	si
	loop	cmkye3			; do all new chars
cmkye4:	mov	word ptr [si],'  '	; add trailing space, clear old next
	inc	si
	mov	cmrawptr,si
	pop	si
	pop	bx			; bx = keyword structure
	inc	cmrawptr		; bufdel backs up one char
	jmp	bufdel
cmkyesc	endp

esceoc	proc	near			; do normal escape end-of-command
	push	ax
	push	dx
	mov	ah,conout		; ring the bell
	mov	dl,bell
	int	dos
	pop	dx
	pop	ax
	call	bufreset		; reset buffer
	stc				; say error condition
	ret
esceoc	endp

; Help. Question mark entered by user.  Display all the keywords that match
; user text. If text is null then use external help if available; otherwise,
; display all keywords in the table. Removes question mark from buffer and
; invokes reparse of command line to-date. User word starts at cmsptr and
; is cmsiz bytes long.
cmkyhlp	proc	near
	xor	cx,cx			; clear number of keyword (none yet)
	cmp	cmsiz,0			; user text given?
	jne	cmkyh1			; ne = yes, use matching keywords
	cmp	cmhlp,0			; external help given?
	jne	cmkyh6			; yes, use it instead of full table
cmkyh1:	mov	temp,0			; count # chars printed on this line
	mov	bx,cmptab		; beginning of kw table
	mov	ch,[bx]			; length of table
	xor	cl,cl			; no keywords or help displayed yet
	inc	bx			; point at CNT field
cmkyh2:	cmp	cmsiz,0			; length of user word
	je	cmkyh3			; e = null, use full table
	call	cmpwrd			; compare keyword with user word
	jc	cmkyh5			; c = no match, get another keyword
cmkyh3:	mov	ax,[bx]			; length of table keyword
	add	byte ptr temp,al	; count chars printed so far
	cmp	temp,76			; will this take us beyond column 78?
	jbe	cmkyh4			; be = no, line has more room
	mov	byte ptr temp,al	; reset the count
	mov	ah,prstr
	mov	dx,offset crlf		; break the line
	int	dos
cmkyh4:	or	cl,cl			; any keywords found yet?
	jnz	cmkyh4a			; nz = yes
	mov	dx,offset cmin01	; start with One of the following: msg
	mov	ah,prstr
	int	dos
	inc	cl			; say one keyword has been found
cmkyh4a:mov	dl,spc			; put two spaces before each keyword
	mov	ah,conout
	int	dos
	int	dos
	add	temp,2			; count output chars
	mov	di,bx			; get current keyword structure
	add	di,2			; text part
	push	cx
	mov	cx,[bx]			; string length to cx, offset to di
	call	fprtscr			; display counted string
	pop	cx
cmkyh5:	dec	ch			; are we at end of table?
	jle	cmkyh7			; le = yes, quit now
	add	bx,[bx]			; next keyword, add CNT chars to bx
	add	bx,4			; skip CNT and 16 bit value
	jmp	cmkyh2			; go examine this keyword

cmkyh6:	mov	si,cmhlp		; external help text in seg data1
	xor	bx,bx			; line counter
	push	es
	mov	ax,seg data1		; all help text is in data1
	mov	es,ax
	cld
cmkyh10:mov	al,es:[si]		; read a help msg byte
	inc	si
	cmp	al,'$'			; end of message?
	je	cmkyh14			; e = yes, stop
cmkyh11:mov	ah,conout
	mov	dl,al
	int	dos			; display byte
	cmp	dl,LF			; line break?
	jne	cmkyh10			; ne = no
	inc	bl			; count line
	cmp	bl,dos_bottom		; (24) time for a more msg?
	jbe	cmkyh10			; be = not yet
	xor	bl,bl			; reset line count
	call	fiseof			; are we at EOF, such as from disk?
	jc	cmkyh10			; c = yes, ignore more msg
	mov	ah,prstr
	mov	dx,offset moremsg	; "... more..." msg
	int	dos
cmkyh13:mov	ah,coninq		; read the char from file, not device
	int	dos
	cmp	al,3			; a ^C?
	je	short cmkyh14		; e = yes, stop the display
	push	bx			; save line counter
	push	es			; and read pointer
	push	si
	call	fctlu			; clear display's line, reuse it
	pop	si
	pop	es
	pop	bx
	jmp	short cmkyh10		; continue
cmkyh14:pop	es
	inc	cl			; say gave help already

cmkyh7:	or	cl,cl			; found any keywords?
	jnz	cmkyh9			; nz = yes
	mov	cx,cmsiz		; length of word
	or	cx,cx
	jg	cmkyh8			; g = something to show
	push	dx
	mov	ah,prstr
	mov	dx,offset cmer01	; command word expected
	int	dos
	pop	dx
	jmp	prserr
cmkyh8:	mov	kwstat,0		; set keyword not-found status
	call	cmskw			; display offending keyword
cmkyh9:	mov	ah,prstr		; start a fresh line
	mov	dx,offset crlf
	int	dos
	call	bufdel			; unwrite the "?" (cmrptr is there)
	ret
cmkyhlp	endp

; See if keyword is ambiguous or not from what the user has typed in.
; Return carry set if word is ambiguous or not found, carry clear otherwise.
; Uses table pointed at by cmptab, user text pointed at by cmsptr and length
; in cmsiz.
cmambg	proc	near
	push	bx
	push	cx
	push	dx
	xor	dl,dl			; count keyword matches so far
	mov	bx,cmptab		; look at start of keyword table
	mov	cl,[bx]			; get number of entries in table
	xor	ch,ch			; use cx as a counter
	jcxz	cmamb8			; z = no table so always ambiguous
	inc	bx			; look at CNT byte of keyword
cmamb4:	call	cmpwrd			; user vs table words, same?
	jc	cmamb6			; c = no match
	inc	dl			; count this as a match
	cmp	dl,1			; more than one match?
	ja	cmamb8			; a = yes, quit now
cmamb6:	add	bx,[bx]			; add CNT chars to bx
	add	bx,4			; skip CNT and 16 bit value
	loop	cmamb4			; do rest of keyword table
	cmp	dl,1			; how many matches were found?
	jne	cmamb8			; ne = none or more than 1: ambiguous
	pop	dx			; restore main registers
	pop	cx
	pop	bx
	clc
	ret				; ret = not ambiguous
cmamb8:	pop	dx			; restore main registers
	pop	cx
	pop	bx
	stc
	ret				; return ambiguous or not found
cmambg	endp

; Compare user text with keyword, abbreviations are considered a match.
; Enter with bx pointing at keyword table CNT field for a keyword.
; Return carry clear if they match, set if they do not match. User text
; pointed at by cmsptr and length is in cmsiz.
; Registers preserved.

cmpwrd	proc	near
	push	cx
	mov	cx,cmsiz		; length of user's text
	jcxz	cmpwrd2			; z: null user word matches no keyword
	cmp	cx,[bx]			; user's text longer than keyword?
	ja	cmpwrd2			; a = yes, no match
	push	ax
	push	bx
	push	si
	add	bx,2		    	; point at table's keyword text
	mov	si,cmsptr		; buffer ptr to user input
	cld
cmpwrd1:lodsb				; user text
	mov	ah,[bx]			; keyword text
	inc	bx			; next keyword letter
	call	ftolowr			; force lower case on both chars
	cmp	ah,al			; same?
	loope	cmpwrd1			; e = same so far
	pop	si
	pop	bx
	pop	ax
	jne	cmpwrd2			; ne = mismatch
	pop	cx			; recover keyword counter
	clc				; they match
	ret
cmpwrd2:pop	cx			; recover keyword counter
	stc				; they do not match
	ret
cmpwrd	endp

; Get pointer to keyword structure using user text. Uses keyword table
; pointed at by cmptab and cmsiz holding length of user's keyword (cmpwrd
; needs cmsptr pointing at user's keyword and length of cmsiz).
; Structure pointer returned in BX.
; Return carry clear for success and carry set for failure. Modifies BX.
getkw	proc	near
	push	cx
	mov	kwstat,0		; keyword status, set to not-found
	cmp	cmsiz,0			; length of user word, empty?
	je	getkw3			; e = yes, fail
	mov	bx,cmptab		; table of keywords
	mov	cl,[bx]			; number of keywords in table
	xor	ch,ch
	jcxz	getkw3			; z = none, fail
	inc	bx			; point to first
getkw1:	call	cmpwrd			; compare user vs table words
	jc	getkw2			; c = failed to match word, try next
	mov	kwstat,1		; say found one keyword, maybe more
	push	dx
	mov	dx,cmsiz		; users word length
	cmp	[bx],dx			; same length (end of keyword)?
	pop	dx
	je	getkw4			; e = yes, exact match. Done
	call	cmambg			; ambiguous?
	jnc	getkw4			; nc = unique, done, return with bx
	mov	kwstat,2		; say more than one such keyword
getkw2:	add	bx,[bx]			; next keyword, add CNT chars to bx
	add	bx,4			; skip CNT and 16 bit value
	loop	getkw1			; do all, exhaustion = failure
getkw3:	pop	cx
	stc				; return failure
	ret
getkw4:	pop	cx
	clc				; return success
	ret
getkw	endp

; show offending keyword message. Cmsptr points to user word,
; cmsiz has length. Modifies AX, CX, and DX.
cmskw	proc	near
	cmp	comand.cmquiet,0	; Quiet mode?
	je	cmskw0			; e = no, regular mode
	ret				; else say nothing
cmskw0:	mov	ah,prstr		; not one of the above terminators
	mov	dx,offset cmer02	; '?Word "'
	int	dos
	mov	ah,conout
	mov	cx,cmsiz		; length of word
	jcxz	cmskw3			; z = null
	mov	ah,conout
	push	si
	mov	si,cmsptr		; point to word
	cld
cmskw1:	lodsb
	cmp	al,' '			; control code?
	jae	cmskw2			; ae = no
	push	ax
	mov	dl,5eh			; caret
	int	dos
	pop	ax
	add	al,'A'-1		; plus ascii bias
cmskw2:	mov	dl,al			; display chars in word
	int	dos
	loop	cmskw1
	pop	si
cmskw3:	mov	dx,offset cmer03	; '" not usable here.'
	cmp	kwstat,1		; kywd status from getkw, not found?
	jb	cmskw4			; b = not found, a = ambiguous
	mov	dx,offset cmer04	; '" ambiguous'
cmskw4:	mov	ah,prstr
        int	dos
	ret
cmskw	endp
;;;;;;;;;; end of support routines for keyword parsing.

; Parse	arbitrary text up to a CR. Enter with BX = pointer to output buffer,
; DX pointing to help text. Produces asciiz string. Return updated pointer in
; BX and output size in AX. Leading spaces are omitted unless comand.cmwhite
; is non-zero (cleared upon exit). It does not need to be followed by the
; usual call to confirm the line. Byte comand.cmblen can be used to specify
; the length of the caller's buffer; cleared to zero by this command to
; imply a length of 127 bytes (default) and if zero at startup use 127 bytes.
cmtext	proc	far
	mov	cmptab,bx		; save pointer to data buffer
	mov	cmhlp,dx		; save the help message
	mov	cmsiz,0			; init the char count
	cmp	comand.cmblen,0		; length of user's buffer given?
	jne	cmtxt0			; ne = yes
	mov	comand.cmblen,127	; else set 127 byte limit plus null
cmtxt0:	mov	cmsflg,0ffh		; skip initial spaces
	cmp	comand.cmwhite,0	; allow leading whitespace?
	je	cmtxt1a			; e = no
cmtxt1:	mov	cmsflg,0		; get all spaces
cmtxt1a:call	cmgtch			; get a char
	jnc	cmtxt5			; nc = non-terminator, put in buffer
	cmp	ah,' '			; space?
	je	cmtxt5			; e = yes, record it
	cmp	ah,escape		; escape?
	jne	cmtxt2			; ne = no
	call	esceoc			; do normal escape end-of-command
	jmp	short cmtxt0		; try again

cmtxt2:	cmp	ah,'?'			; asking a question?
	je	cmtxt3			; e = yes
	cmp	ah,cr			; formal carriage return?
	je	cmtxt2a			; e = yes
	inc	cmrptr			; accept char into buffer
	jmp	short cmtxt5		;  and record the char
cmtxt2a:mov	bx,cmptab		; return updated pointer
	xor	ax,ax
	mov	byte ptr[bx],al		; put terminator into the buffer
	mov	comand.cmwhite,al	; clear leading whitespace flag
	mov	comand.cmper,al		; reset to variable recognition
	mov	comand.cmblen,ax	; set user buffer length to unknown
	mov	comand.cmkeep,al
	mov	comand.cmdirect,al	; no more force input from kbd/file
	xchg	ax,cmsiz		; return count in AX
	call	rprompt			; restore master prompt level
	clc
	ret
					; Help processor
cmtxt3:	inc	cmrptr			; count the ?
	cmp	cmsiz,0			; is "?" the first char?
	jne	cmtxt5			; ne = no, just add to buffer
	dec	cmrptr
	cmp	cmhlp,0			; external help given?
	jne	cmtxt3a			; ne = yes
	mov	cmhlp,offset cmin00	; confirm with c/r msg
cmtxt3a:push	cmsiz
	mov	cmsiz,0
	call	cmkyhlp			; display help
	pop	cmsiz
	jmp	cmtxt1a			; keep going

cmtxt5:	inc	cmsiz			; increment the count
	mov	bx,cmptab		; pointer into destination array
	mov	[bx],ah			; put char into the buffer
	inc	bx
	xor	al,al
	mov	[bx],al			; insert null terminator
	mov	cmptab,bx
	mov	cx,cmsiz		; length of command so far
	cmp	cx,comand.cmblen	; buffer filled?
	ja	cmtxt6			; a = yes, declare error
	jb	cmtxt5a			; a = not filled yet
	mov	ah,conout		; notify user that the buffer is full
	mov	dl,bell
	int	dos
cmtxt5a:jmp	cmtxt1
cmtxt6:	mov	ah,prstr
	mov	dx,offset cmer09
	int	dos
	jmp	prserr			; declare parse error
cmtext	endp


; Parse arbitrary text up to whitespace.  Enter with DX pointing to output
; buffer and BX pointing to help text. Produces asciiz string. Return updated
; pointer in DX and input size in AH. Skips leading whitespace unless 
; comand.cmwhite is non-zero (cleared upon exit). Does a return skip exit.

cmfil0	proc	far
	mov	cmptab,dx		; save pointer to data buffer
	mov	cmhlp,bx		; save the help message
	mov	cmsiz,0			; init the char count
	cmp	comand.cmblen,0		; length of user's buffer given?
	jne	cmfil0a			; ne = yes
	mov	comand.cmblen,127	; else set 127 byte limit plus null
cmfil0a:cmp	comand.cmwhite,0	; allow leading whitespace?
	jne	cmfil1			; ne = yes
	mov	cmsflg,0ffh		; omit leading space
cmfil1:	call	cmgtch			; get a char
	jc	cmfi1a			; c = terminator 
	jmp	short cmfil5		; put char into the buffer
cmfi1a:	cmp	ah,escape		; escape?
	je	cmfi1b			; e = yes
	jmp	short cmfil2		; process other terminators
cmfi1b:	call	esceoc			; do normal escape end-of-command
	jmp	short cmfil0a		; try again

cmfil2:	cmp	ah,'?'			; asking a question?
	je	cmfil3			; e = yes
	xchg	dx,bx			; re-interchange bx and dx
	xor	ax,ax
	mov	comand.cmwhite,al	; clear whitespace flag
	mov	comand.cmper,al		; reset to variable recognition
	mov	comand.cmkeep,al	; do not keep Take file open
	mov	comand.cmblen,ax	; set user buffer length to unknown
	mov	bx,cmptab		; pointer into destination array
	mov	byte ptr[bx],al		; put null terminator into the buffer
	inc	bx
	xchg	ax,cmsiz		; return count in AX
	mov	dx,cmptab		; return updated pointer
	xchg	dx,bx			; re-interchange bx and dx
	clc
	ret				; return success

cmfil3:	inc	cmrptr			; count the ?
	cmp	cmsiz,0			; Is "?" first char?
	jne	cmfil5			; ne = no, just add to buffer
	dec	cmrptr
	cmp	cmhlp,0			; external help given?
	jne	cmfil3a			; ne = yes
	mov	cmhlp,offset cmin00	; confirm with c/r msg
cmfil3a:push	cmsiz
	mov	cmsiz,0
	call	cmkyhlp
	pop	cmsiz
	jmp	short cmfil1

cmfil5:	inc	cmsiz			; increment the count
	mov	bx,cmptab		; pointer into destination array
	mov	[bx],ah			; put char into the buffer
	inc	bx
	mov	cmptab,bx
	mov	cx,cmsiz		; length of command so far
	cmp	cx,comand.cmblen	; buffer filled?
	ja	cmfil7			; a = yes, declare error
	jb	cmfil6			; a = not filled yet
	mov	ah,conout		; notify user that the buffer is full
	mov	dl,bell
	int	dos
cmfil6:	jmp	cmfil1

cmfil7:	mov	ah,prstr
	mov	dx,offset cmer09
	int	dos
	jmp	prserr			; declare parse error
cmfil0	endp

; This routine gets a confirm (CR) and displays any extra non-blank text.
; errflag non-zero means suppress "extra text" display in this routine
; because another routine is handling errors.
cmcfrm	proc	far
	mov	comand.cmper,1		; do not react to \%x substitutions
	cmp	cmrptr,offset cmdbuf	; empty buffer?
	je	cmcfr7			; e = yes
	mov	bx,cmrptr		; where to read next
	dec	bx			; last read byte
	cmp	byte ptr [bx],CR	; terminated already?
	je	cmcfr7			; e = yes

cmcfr1:	mov	cmsflg,0ffh		; set space-seen flag (skip spaces)
	call	cmgtch			; get a char
	push	cmrptr
	pop	temp			; remember first non-space position
	jc	cmcfr4			; c = terminator
	dec	temp			; backup to text char
cmcfr3:	mov	cmsflg,0ffh		; set space-seen flag (skip spaces)
	call	cmgtch
	jnc	cmcfr3			; read until terminator
cmcfr4:	cmp	ah,' '
	je	cmcfr3			; ignore ending on space
	cmp	ah,escape		; escape?
	jne	cmcfr5			; ne = no
	call	esceoc			; do standard end of cmd on escape
	mov	ax,cmrptr
	cmp	ax,temp			; started text yet?
	je	cmcfr1			; e = no
	jmp	short cmcfr3		; try again
cmcfr5: cmp	ah,'?'			; curious?
        jne	cmcfr6			; ne = no
	mov	cmhlp,offset cmin00	; msg Confirm with c/r
	mov	cmsiz,0			; no keyword
	mov	errflag,0
	jmp	cmkyhlp			; do help
cmcfr6:	cmp	ah,cr			; the confirmation char?
	jne	cmcfr3			; ne = no
	cmp	errflag,0		; already doing one error?
	jne	cmcfr7			; ne = yes, skip this one
	mov	cx,cmrptr		; pointer to terminator
	mov	dx,temp			; starting place
	sub	cx,dx			; end minus starting point = length
	jle	cmcfr7			; le = nothing to display
	push	dx			; save source pointer
	mov	ah,prstr
	mov	dx,offset cmer07	; ?Ignoring extras
	int	dos
	pop	dx
	mov	bx,1			; stdout handle, cx=count, dx=src ptr
	mov	ah,write2		; allow embedded dollar signs
	int	dos
	mov	ah,prstr
	mov	dx,offset cmer08	; trailer msg
	int	dos
cmcfr7:	mov	errflag,0
	mov	comand.cmper,0		; reset to variable recognition
	mov	comand.cmkeep,0		; don't keep take files open now
	clc				; return confirmed
	ret
cmcfrm	endp

;;; Routines to get and edit incoming text.

; Detect '\%x' (x = '0' or above) and substitute the matching Macro string
; in place of the '\%x' phrase in the user's buffer. If comand.cmper != 0
; then treat '\%' as literal characters. If no matching parameter exists
; just remove '\%x'. Ditto for \v(variable). Returns carry clear if nothing
; done, else carry set and new text already placed in user's buffer.
; Includes \v(variable) and \$(Environment variable) and \m(macro name).
; Uses depth-first recursion algorithm. All registers preserved.
subst	proc	near
	cmp	comand.cmper,0		; recognize '\%','\v(','\$(','\m(' ?
	jne	subst0			; ne = no, treat as literals
	cmp	taklev,0		; in a Take file?
	je	subst0a			; e = no
	push	bx
	mov	bx,takadr
	cmp	[bx].takper,0		; expand macros?
	pop	bx
	je	subst0a			; e = yes
subst0:	clc				; report out to application
	ret
subst0a:cmp	ah,'\'			; is it the first char of the pattern?
	jne	subst1			; ne = no, try next
	cmp	subcnt,1		; \\?
	je	subst1a			; e = yes
	cmp	endedbackslash,0	; ended sub macro on backslash?
	jne	subst1a			; ne = yes
	inc	subcnt			; say first char (\) is matched
	and	subcnt,1		; modulo 2
	mov	subtype,ah
	stc				; do not pass to application
	ret

subst1:	cmp	subcnt,1		; first char matched already?
	ja	subst3			; a = first two have been matched
	jb	subst0			; b = none yet
	mov	al,ah			; test kind of substitution
	or	al,20h			; convert to lower case
	cmp	ah,'%'			; second match char, same?
	je	subst2			; e = yes
	cmp	al,'v'			; \v(...)?
	je	subst2			; e = yes
	cmp	al,'$'			; \$(..)?
	je	subst2			; e = yes
	cmp	al,'m'			; \m(..)?
	je	subst2			; e = yes
	cmp	al,'\'			; '\\'?
	je	subst1a			; e = yes, pass back both slashes
	cmp	al,'f'			; \fname(..)?
	jne	subst1a			; ne = no
	mov	subtype,al		; remember type
	inc	subcnt			; count match
	jmp	subst10			; read more bytes internally

subst1a:push	ax			; replay bytes as literals
	mov	al,subcnt		; previously matched bytes
	xor	ah,ah
	inc	ax			; plus current byte
	sub	cmrptr,ax
	mov	replay_cnt,ax		; trailer bytes, amount to replay
	pop	ax
	mov	subcnt,0		; mismatch, clear match counter
	mov	endedbackslash,0
	stc				; reread from input
	ret

subst2:	cmp	al,'%'			; starting \%?
	jne	subst2b			; ne = no
	cmp	subtype,'v'		; doing \v(..)? (dirs of c:\%foobar)
;;;	je	subst1a			; e = yes, don't expand \% inside

subst2b:mov	subtype,al		; remember kind of substitution
	inc	subcnt			; count match
	stc				; do not pass to application
	ret

subst3:	cmp	subtype,'v'		; doing \v(..)?
	je	subst3a			; e = yes
	cmp	subtype,'$'		; doing \$(..)?
	je	subst3a			; e = yes
	cmp	subtype,'m'		; doing \m(..)?
	jne	subst3b			; ne = no, do \%<char>
subst3a:cmp	ah,'('			; have leading parenthesis?
	jne	subst1a			; ne = no, mismatch, exit
	jmp	subst10			; process \v(..), \$(..), \m(..)
subst3b:mov	subcnt,0		; do \%<char>, clear match counter
	cmp	ah,'0'			; third char is '0' or above?
	jb	subst1a			; b = out of range, no match
	push	bx			; save working regs
	push	cx
	push	es
	sub	cmrptr,3		; reread commands where backslash was
	call	bufreset		; reset buffer to this point
	push	cmptab			; save current keyword parsing parms
	push	cmsptr
	push	cmsiz
	mov	bx,cmrptr		; points at backslash
	mov	cmsptr,bx		; direct keyword routine to it
	mov	cmsiz,3			; three bytes (\%x) of user text
	mov	cmptab,offset mcctab 	; use Macro table for new text
	call	getkw			; get ptr, bx, to matching keyword
	pop	cmsiz			; restore borrowed keyword parameters
	pop	cmsptr
	pop	cmptab
	jc	substx			; c = not found, keep after pops
	cmp	taklev,maxtak		; room in take level?
	jae	subst6			; ae = no
	mov	cx,[bx]			; length of found word
	add	cx,2			; plus count field
	add	bx,cx			; point to 16 bit value (string ptr)
	mov	es,[bx]			; point to string structure segment
	xor	bx,bx
	mov	cx,es:[bx]		; length of string
	call	takopen_sub		; open take as text substitution
	mov	bx,takadr		; pointer to new Take structure
	mov	[bx].takbuf,es		; segment of Take buffer
	mov	[bx].takcnt,cx		; number of unread bytes
	
substx:	pop	es
	pop	cx
	pop	bx
	stc				; carry set to say reread material
	ret

subst6:	mov	ah,prstr
	mov	dx,offset stkmsg	; out of work space msg
	int	dos
	jmp	prserr			; and declare parse error

					; \fname(..), \m(..), \v(..), \$(..)
subst10:push	bx			; save working regs
	push	cx
	push	es
	push	cmptab			; save current keyword parsing parms
	push	cmsptr
	push	cmsiz
	push	dx
	mov	subcnt,0		; clear match counter
	mov	cmhlp,0
	mov	ax,cmrptr
	mov	valtmp,ax		; remember current read pointer
	mov	cmsptr,ax		; start of word
	mov	cmptab,offset valtab	; table of keywords for \v(..)
	cmp	subtype,'v'		; \v(..)?
	je	subst10a		; e = yes
	mov	cmptab,offset envtab	; Environment variable table \$(..)
	cmp	subtype,'$'		; \$(..)?
	je	subst10a		; e = yes
	mov	cmptab,offset evaltab	; evaluate (name) table
	cmp	subtype,'f'		; \fname()?
	je	subst10a		; e = yes
	mov	cmptab,offset mcctab	; main Macro table for \m(..)
subst10a:mov	cmsflg,0		; see leading spaces/tabs
	mov	cmsiz,0			; word size

subst11:mov	ch,subtype		; save \type
	push	cx
	call	cmgtch			; read a character into ah
	rcl	al,1			; put carry bit into low bit of al
	pop	cx
	xchg	ch,subtype		; recover \type
	rcr	al,1			; recover carry bit
	jnc	subst13			; nc = non-terminator
	cmp	subtype,'m'		; discard trailing spaces for these
	je	subst11b
	cmp	subtype,'v'
	je	subst11b
	cmp	subtype,'$'
	jne	subst11c
subst11b:cmp	ah,' '			; space terminator?
	je	subst11			; e = yes, ignore it
subst11c:cmp	ah,'?'			; need help?
	jne	subst11a		; ne = no
	call	cmkyhlp			; display help information
	jmp	short subst11

subst11a:cmp	ah,escape		; escape?
	jne	subst12			; ne = no
	call	cmkyesc			; process escape
	jmp	short subst11		; failed, ignore esc, read more
					;
subst12:cmp	subtype,'f'		; \fname(..)?
	je	subst12a		; e = yes, else no need to replay
	jmp	subst17

subst12a:mov	bx,cmsptr		; where \fname() word started
	sub	bx,2			; back over "\f" part
	mov	cx,cmrptr		; last read + 1
	sub	cx,bx			; bytes in \foobar
	mov	cmrptr,bx		; reread point is the \
	mov	replay_cnt,cx		; bytes to reread
	jmp	subst17

subst13:inc	cmsiz			; count user chars
	cmp	subtype,'f'		; \fname(...)?
	jne	subst13a		; ne = no
	cmp	ah,'('			; look for "(" as terminator
	je	short subst13b		; e = found it, lookup "name" as keyword
	cmp	ah,')'			; premature ")"?
	jne	subst11			; ne = no, keep reading
	jmp	short subst12a		; yes, stop here

subst13a:cmp	ah,')'			; end bracket?
	jne	subst11			; ne = no, keep looking
subst13b:dec	cmsiz			; omit user's ')' from tests
	cmp	subtype,'$'		; \$(..)?
	je	subst13c		; e = yes, no keyword in table
	push	cmsptr			; save pointer
subst13g:mov	bx,cmsptr
	cmp	byte ptr [bx],' '	; leading spaces?
	jne	subst13h		; ne = no
	inc	cmsptr			; look at next char
	jmp	short subst13g
subst13h:call	getkw			; \m(..) and \v(..) test for keyword
	pop	cmsptr
	jc	subst12			; c = failure
	jmp	short subst13d		; success

subst13c:call	envvar			; search Environment for the word
	jc	subst12			; c = failure
	mov	bx,V_environ		; set bx to kind of value for valtoa
	jmp	short subst13f

subst13d:cmp	subtype,'f'		; doing \fname(...)?
	jne	subst13f		; ne = no
	mov	subcnt,0		; clear \f match indicator
	add	bx,[bx]			; add length of keyword text (CNT)
	add	bx,2			; point at value field
	mov	bx,[bx]			; get value
subst13e:mov	evaltype,bx		; remember kind of operation
	call	evaltoa			; do \fname argument evaluation
	jc	subst12a		; error, reparse
	jmp	short subst17

subst13f:mov	ax,valtmp		; where word started
	sub	ax,3			; backup to "\v(" or "\$("
	mov	cmrptr,ax		; write output where backslash was
	call	bufreset		; resets cmwptr too
	xor	dx,dx			; signal valtoa to not add trailing sp
	cmp	subtype,'$'		; \$(..)?
	je	subst14			; e = yes, no keyword structure
	mov	cx,[bx]			; bx = structure pointer, cx=keyw len
	add	cx,2			; skip count byte
	add	bx,cx			; point at 16 bit value field
	mov	bx,[bx]			; get value to bx for valtoa
subst14:cmp	taklev,maxtak		; room in take level?
	jb	subst15			; b = yes
	mov	dx,offset stkmsg	; out of work space msg
	mov	ah,prstr		; display error message
	int	dos
	jmp	short subst17

subst15:mov	subcnt,0		; clear match indicator
	call	takopen_sub		; open take as text subsitution
	jc	subst17			; c = failed
	mov	cx,bx			; value command kind, save
	mov	ax,tbufsiz		; bytes of buffer space wanted
	call	malloc
	jc	subst17			; c = failed
	mov	bx,takadr		; point to structure
	or	[bx].takattr,take_malloc ; remember to dispose via takclos
	mov	[bx].takbuf,ax		; seg of buffer
	mov	es,ax			; ES:DI will be buffer pointer
	mov	di,[bx].takptr		; where to write next
	mov	[bx].takper,0		; expand macros
	cmp	subtype,'m'		; \m(..)?
	jne	subst16a		; ne = no
	mov	[bx].takper,1		; do not expand macros in \m(..)
subst16a:mov	bx,cx			; value command
	call	valtoa			; make text be an internal macro
	mov	bx,takadr
	mov	[bx].takcnt,di		; length
subst17:pop	dx
	mov	subtype,0
	pop	cmsiz			; restore borrowed keyword parameters
	pop	cmsptr
	pop	cmptab
	pop	es
	pop	cx
	pop	bx
	stc				; carry = signal reread source
	ret
subst	endp

; Make an internal macro defined as the text for one of the value variables.
; Use incoming DX as trailing space suppression flag, if null.
valtoa	proc	near
	push	di			; save starting di
	push	dx			; save trailing space flag
	mov	word ptr es:[di],0	; fill buffer with sweet nothings
					; BX has index of variable
	cmp	bx,V_environ		; \$() Environment?
	jne	valtoa1			; ne = no
	mov	cx,envlen		; string length
	jcxz	valtoa0			; z = empty
	cmp	cx,tbufsiz-2		; greater than current buffer?
	jbe	valtoa0a		; be = no
	push	cx
	mov	ax,[bx].takbuf		; old buffer
	mov	es,ax			; new ES from above
	mov	ah,freemem		; free it
	int	dos
	mov	ax,cx			; bytes wanted
	call	malloc			; get more space
	mov	bx,takadr
	mov	[bx].takbuf,ax		; seg of macro def
	mov	ES,ax			; new ES
	or	[bx].takattr,take_malloc ; remember to dispose via takclos
	pop	cx
valtoa0a:
	push	si
	push	ds
	lds	si,envadr		; ds:si is source from Environment
	cld
	rep	movsb			; copy string
	pop	ds
	pop	si
valtoa0:jmp	valtoa90
	
valtoa1:cmp	bx,V_argc		; \v(argc)?
	jne	valtoa2			; ne = no
	call	wrtargc			; write argc
	jmp	valtoa90
valtoa2:cmp	bx,V_count		; \v(count)?
	jne	valtoa3			; ne = no
	call	wrtcnt			; write it
	jmp	valtoa90
valtoa3:cmp	bx,V_date		; \v(date)?
	jne	valtoa4
	call	wrtdate
	jmp	valtoa90
valtoa4:cmp	bx,V_errlev		; \v(errorlevel)?
	jne	valtoa5			; ne = no
	call	wrterr
	jmp	valtoa90
valtoa5:cmp	bx,V_dir		; \v(dir)?
	jne	valtoa6
	call	wrtdir
	jmp	valtoa90
valtoa6:cmp	bx,V_time		; \v(time)?
	jne	valtoa7
	call	wrttime
	jmp	valtoa90
valtoa7:cmp	bx,V_version		; \v(version)?
	jne	valtoa8			; ne = no
	mov	ax,version		; get version such as 300
	call	fdec2di			; convert binary to asciiz
	jmp	valtoa90
valtoa8:cmp	bx,V_platform		; \v(platform)?
	jne	valtoa9			; ne = no
	call	wrtplat			; get machine name, e.g. "IBM-PC"
	jmp	valtoa90
valtoa9:cmp	bx,V_system		; \v(system)?
	jne	valtoa10		; ne = no
	call	wrtsystem		; get "MS-DOS" string
	jmp	valtoa90
valtoa10:cmp	bx,V_kbd		; \v(keyboard)?
	jne	valtoa11		; ne = no
	call	wrtkbd			; 88 or 101 value
	jmp	valtoa90
valtoa11:cmp	bx,V_speed		; \v(speed)?
	jne	valtoa12		; ne = no
	push	di
	call	fgetbaud		; read baud rate from hardware
	pop	di
	mov	bx,portval
	mov	ax,[bx].baud
	cmp	al,byte ptr bdtab	; index versus number of table entries
	jb	valtoa11a		; b = index is in the table
	mov	si,offset cmunk-2	; unrecognized value, say "unknown"
	mov	bx,7			; length of string
	jmp	short valtoa11c
valtoa11a:mov	si,offset bdtab		; ascii rate table
	mov	cl,[si]			; number of entries
	inc	si			; point to an entry
valtoa11b:
	mov	bx,[si]			; length of text string
	cmp	ax,[si+bx+2]		; our index vs table entry index
	je	valtoa11c		; e = match
	add	si,bx			; skip text
	add	si,4			; skip count and index word
	loop	valtoa11b		; look again
	mov	si,offset cmunk-2	; unrecognized value, say "unknown"
	mov	bx,7			; length of string
valtoa11c:mov	cx,bx			; length of string
	add	si,2			; point at string
	rep	movsb			; copy string
	jmp	valtoa90

valtoa12:cmp	bx,V_program		; \v(program)?
	jne	valtoa13		; ne = no
	call	wrtprog			; get "MS-DOS_KERMIT" string
	jmp	valtoa90
valtoa13:cmp	bx,V_status		; \v(status)?
	jne	valtoa14		; ne = no
	call	wrtstat			; compose status string
	jmp	valtoa90
valtoa14:cmp	bx,V_ndate		; \v(ndate)?
	jne	valtoa15		; ne = no
	call	wrtndate
	jmp	valtoa90
valtoa15:cmp	bx,V_port		; \v(port)? or \v(line)?
	jne	valtoa16		; ne = no
	call	wrtport
	jmp	valtoa90
valtoa16:cmp	bx,V_terminal		; \v(terminal)?
	jne	valtoa17		; ne = no
	call	wrtterm
	jmp	valtoa90
valtoa17:
ifndef	no_tcp
	cmp	bx,V_session		; \v(session) (internal Telnet)?
	jne	valtoa18		; ne = no

	mov	ax,sescur		; get internal Telnet session ident
	inc	ax			; count from 1 for users (0 == none)
	call	fdec2di			; convert binary to asciiz
	jmp	valtoa90
endif	; no_tcp
valtoa18:cmp	bx,V_ntime		; \v(ntime) (seconds in day)?
	jne	valtoa19		; ne = no
	mov	ah,gettim		; get DOS time of day
	int	dos			; ch=hh, cl=mm, dh=ss, dl=0.01 sec
	mov	bx,60
	mov	al,ch			; hours
	mul	bl			; to minutes
	xor	ch,ch
	add	ax,cx			; plus minutes
	mov	cl,dh			; preserve seconds
	mul	bx			; need carry out to DX
	add	ax,cx			; add seconds
	adc	dx,0
	mov	cx,10			; down to 16 bit size
	div	cx
	call	fdec2di			; convert binary to asciiz
	xchg	ax,dx
	call	fdec2di			; convert binary to asciiz
	jmp	valtoa90
valtoa19:cmp	bx,V_dosver		; \v(dosversion)?
	jne	valtoa20		; ne = no
	mov	ax,dosnum		; DOS verson, major high, minor low
	push	ax
	xchg	ah,al
	xor	ah,ah
	call	fdec2di			; write major
	pop	ax
	xor	ah,ah
	cmp	al,10			; less than 10?
	ja	valtoa19a		; a = no
	mov	byte ptr es:[di],'0'	; use two digits for minor
	inc	di
valtoa19a:
	call	fdec2di			; write minor
	jmp	valtoa90
valtoa20:
ifndef	no_tcp
	cmp	bx,V_tcpip		; \v(tcp_status)?
	jne	valtoa21		; ne = no
					; SUCCESS	0
					; NO_DRIVER	1
					; NO_LOCAL_ADDRESS 2
					; BOOTP_FAILED	3
					; RARP_FAILED	4
					; BAD_SUBNET_MASK 5
					; SESSIONS_EXCEEDED 6
					; HOST_UNKNOWN	7
					; HOST_UNREACHABLE 8
					; CONNECTION_REJECTED 9
	mov	ax,tcp_status		; get tcp status, if any
	call	fdec2di			; write value
	jmp	valtoa90
endif	; no_tcp

valtoa21:cmp	bx,V_parity		; \v(parity)?
	jne	valtoa22		; ne = no
	mov	bx,portval
	mov	bl,[bx].parflg		; parity
	xor	bh,bh
	shl	bx,1			; address words
	cld
	mov	si,parmsgtab[bx]	; offset of parity name string
valtoa21a:lodsb
	stosb
	or	al,al			; end of string?
	jnz	valtoa21a		; nz = no
	mov	word ptr es:[di-1],0020h ; space, null
	jmp	valtoa90

valtoa22:cmp	bx,V_carrier		; \v(carrier)?
	jne	valtoa23		; ne = no
	mov	bl,flags.carrier	; carrier
	and	bl,1			; just one bit
	xor	bh,bh
	shl	bx,1			; address words
	cld
	mov	si,onoffmsgtab[bx]	; offset of carrier string
valtoa22a:lodsb
	stosb
	or	al,al			; end of string?
	jnz	valtoa22a		; nz = no
	jmp	valtoa90

valtoa23:cmp	bx,V_prompt		; \v(prompt)?
	jne	valtoa24		; ne = no
	push	si
	mov	si,prmptr		; current prompt raw text
	cld
valtoa23a:lodsb				; read a byte
	stosb				; store
	or	al,al			; end of string?
	jnz	valtoa23a		; z = no
	pop	si
	dec	di			; don't show trailing null
	mov	word ptr es:[di],0020h	; space, null
	jmp	valtoa90

valtoa24:cmp	bx,V_cps		; \v(cps)?
	jne	valtoa25		; ne = no
	push	di
	mov	di,offset decbuf+200	; must be in DS data seg
	call	shovarcps		; use worker in msssho.asm
	mov	si,offset decbuf+200	; copy this buffer to es:di buffer
	pop	di
	cld
	mov	cx,7			; limit loop
valtoa24a:lodsb				; read result
	or	al,al			; terminator?
	jz	valtoa24b		; z = yes, stop
	stosb
	loop	valtoa24a
valtoa24b:jmp	valtoa90

valtoa25:cmp	bx,V_space		; \v(space)?
	jne	valtoa26		; ne = no
	xor	cx,cx			; drive letter (null means current)
	call	fdskspace		; compute space, get letter into CL
	jnc	valtoa25a		; nc = success
	xor	ax,ax
	xor	dx,dx
valtoa25a:
	push	di
	push	es
	mov	di,ds
	mov	es,di
	mov	di,offset cmdbuf-30-cmdblen
	call	flnout
	mov	dx,offset cmdbuf-30-cmdblen
	call	fstrlen
	mov	si,dx
	pop	es
	pop	di
	rep	movsb
	mov	word ptr [di],0020h	; space, null
	jmp	valtoa90

valtoa26:cmp	bx,V_startup		; \v(startup)?
	jne	valtoa27		; ne = no
	push	si
	mov	si,offset startup	; startup directory string
	mov	dx,si
	call	fstrlen			; get length to cx
	rep	movsb			; copy string
	pop	si
	mov	word ptr es:[di],0020h	; space, null
	jmp	valtoa90

valtoa27:cmp	bx,V_console		; \v(console)?
	jne	valtoa28		; ne = no
	mov	ax,1a00h		; get ANSI.SYS installed state
	int	2fh
	mov	si,offset ansiword	; assume installed
	or	al,al			; installed?
	jnz	valtoa27a		; nz = yes
	mov	si,offset noneword	; say "NONE"
valtoa27a:lodsb				; read a byte
	stosb				; store a byte
	or	al,al			; at end of string?
	jnz	valtoa27a		; nz = no
	mov	word ptr es:[di-1],0020h ; space, null
	jmp	valtoa90

valtoa28:cmp	bx,V_monitor		; \v(monitor)?
	jne	valtoa29		; ne = no
	mov	si,offset colormsg	; assume color monitor
	cmp	crt_mode,7		; mono text
	jne	valtoa28a		; ne = no
	mov	si,offset monomsg	; say mono
valtoa28a:jmp	short valtoa27a		; copy material

valtoa29:cmp	bx,V_cmdlevel		; \v(cmdlevel)?
	jne	valtoa30		; ne = no
	mov	al,taklev		; take level
	xor	ah,ah
	call	fdec2di			; write value
	jmp	valtoa90

valtoa30:cmp	bx,V_input		; \v(input)?
	jne	valtoa31		; ne = no
	push	si
	push	ds
	push	es
	call	buflog			; get INPUT buffer pointers
	mov	dx,input_read		; bytes read
	cmp	dx,cx			; more than a buffer?
	jbe	valtoa30g		; be = no
	mov	dx,cx			; limit to one buffer's worth
valtoa30g:sub	si,dx			; next-read - have read count
	jnc	valtoa30a		; nc = no wrap from beginning
	add	si,cx			; unwrap (+ scpbuflen)

valtoa30a:mov	bx,cx			; length of buffer (and max offset)
	mov	cx,dx			; bytes to report
	cmp	cx,tbufsiz-2		; larger than our report buffer?
	jbe	valtoa30b		; be = no
	mov	cx,tbufsiz-2		; limit to our report buffer
valtoa30b:mov	ax,es			; seg of input buffer
	pop	es
	mov	DS,ax			; ds:si is input buffer
	jcxz	valtoa30e		; z = nothing to report
	cld
	xor	dx,dx			; start writing flag
valtoa30c:lodsb				; read a byte
	cmp	al,LF			; look for terminators
	je	valtoa30h
	cmp	al,CR
	je	valtoa30h
	cmp	al,FF
	jne	valtoa30f
valtoa30h:or	dx,dx			; written anything yet?
	jz	valtoa30d		; z = no, skip these guys, continue
	jmp	short valtoa30e		; exit on terminator
valtoa30f:stosb				; report byte
	inc	dx			; say have written
	cmp	si,bx			; beyond end of Input buffer?
	jb	valtoa30d		; b = not yet
	xor	si,si			; reset to start of INPUT-buf (wrap)
valtoa30d:loop	valtoa30c
valtoa30e:pop	ds
	pop	si
	jmp	valtoa90

valtoa31:cmp	bx,V_query		; \v(query)?
	jne	valtoa32		; ne = no
	mov	word ptr es:[di],0020h 	; space, null
	cmp	queryseg,0		; is there an malloc'd seg to use?
	je	valtoa90		; e = no
	push	es
	mov	si,queryseg
	mov	es,si
	mov	cx,es:[0]		; get length of string
	pop	es
	push	si
	cmp	cx,tbufsiz-2		; greater than current buffer?
	jbe	valtoa31a		; be = no
	push	cx
	mov	ax,[bx].takbuf		; old buffer
	mov	es,ax			; new ES from above
	mov	ah,freemem		; free it
	int	dos
	mov	ax,cx			; bytes wanted
	call	malloc			; get more space
	mov	bx,takadr
	mov	[bx].takbuf,ax		; seg of macro def
	mov	ES,ax			; new ES
	or	[bx].takattr,take_malloc ; remember to dispose via takclos
	pop	cx

valtoa31a:push	ds
	mov	si,queryseg		; get segment of query string
	mov	ds,si
	mov	si,2			; skip count word
	cld
	rep	movsb			; copy to es:di
	pop	ds
	pop	si
	jmp	valtoa90

valtoa32:cmp	bx,V_sysid		; \v(sysid)?
	jne	valtoa33		; ne = no
	mov	ax,'8U'			; always report U8
	stosw
	jmp	valtoa90

valtoa33:cmp	bx,V_charset		; \v(charset)?
	jne	valtoa34		; ne = no
	mov	ax,'PC'
	stosw				; CP prefix
	mov	ax,flags.chrset		; get current Code Page
	call	fdec2di
	jmp	valtoa90

valtoa34:cmp	bx,V_inpath		; \v(inpath)?
	jne	valtoa35		; ne = no
	mov	si,inpath_seg		; get segment of in_path string
	or	si,si			; anything there?
	jz	valtoa34b		; z = no
	push	ds
	mov	ds,si
	xor	si,si
	cld
	lodsw				; get count word
	mov	cx,ax
	cmp	cx,64			; keep it reasonable
	jbe	valtoa34a		; be = ok
	mov	cx,64
valtoa34a:rep	movsb			; copy to es:di
	dec	di			; don't include trailing null from env
	pop	ds
valtoa34b:jmp	short valtoa90

valtoa35:cmp	bx,V_disk		; \v(disk)?
	jne	valtoa36		; ne = no
	mov	ah,gcurdsk		; get current disk
	int	dos
	add	al,'A'			; make 1 == A (not zero)
	xor	ah,ah
	cld
	stosw
	jmp	short valtoa90

valtoa36:cmp	bx,V_cmdfile		; \v(cmdfile)?
	jne	valtoa37		; ne = no
	mov	si,offset cmdfile	; path of last Take file
	mov	dx,si
	call	fstrlen
	cld
	rep	movsb
	jmp	short valtoa90

valtoa37:cmp	bx,V_inidir		; \v(inidir)?
	jne	valtoa80		; ne = no
	mov	si,offset inidir	; path of mskermit.ini
	mov	dx,si
	call	fstrlen
	cld
	rep	movsb
	jmp	short valtoa90

valtoa80:push	bx			; \m(macro_name)
	push	es
	mov	di,bx			; save seg of macro def
	mov	bx,takadr
	test	[bx].takattr,take_malloc ; buffer already allocated?
	jz	valtoa80a		; z = no
	push	es
	mov	ax,[bx].takbuf		; old buffer
	or	ax,ax			; if any allocated
	jz	valtoa80b		; z = none
	mov	es,ax
	mov	ah,freemem		; free it
	int	dos
valtoa80b:and	[bx].takattr,not take_malloc ; say no more freeing needed
	pop	es
valtoa80a:
	mov	[bx].takbuf,di		; seg of macro def
	mov	[bx].takptr,2		; offset of two
	mov	es,di
	mov	cx,es:[0]		; get length of string
	pop	es
	pop	bx
	pop	dx
	pop	di
	mov	di,cx			; report length in di
	clc
	ret
valtoa90:pop	dx			; trailing space flag
	or	dx,dx			; leave the spaces?
	jnz	valtoa91		; nz = yes
	cmp	word ptr es:[di-1],0020h ; trailing space?
	jne	valtoa91		; ne = no
	dec	di			; remove space
valtoa91:pop	ax			; saved starting di
	sub	di,ax			; di = length of the buffer contents
	clc
	ret
valtoa	endp

; Far callable version
fvaltoa	proc	far
	call	valtoa
	ret
fvaltoa	endp

; Make an internal macro defined as the text for one of the value variables.
; Use incoming DX as trailing space suppression flag, if null.
; BX has keyword value (table envtab)
; If these fail they consume their text quietly.
evaltoa	proc	near
	push	dx			; save trailing space flag
	push	si
	push	temp			; save work variable
	mov	cmsiz,0			; word size
	mov	temp,1			; "()" brace counter, one seen

evalt1:	mov	dl,comand.cmper
	push	dx
	mov	cmsflg,0		; see leading spaces/tabs
	cmp	evaltype,F_contents	; \fcontents(macro)?
	je	evalt1a			; e = yes
	cmp	evaltype,F_literal	; \fliteral(string)?
	jne	evalt1b			; ne = no
evalt1a:mov	comand.cmper,1		; arg, treat macros as literals
evalt1b:push	temp
	push	evaltype		; preserve, reading may recurse
	call	cmgtch			; read a character into ah
	pop	evaltype
	pop	temp
	pop	dx
	mov	comand.cmper,dl		; restore macro expansion state
	jnc	evalt5			; nc = non-terminator
	cmp	ah,' '			; just a space?
	je	evalt5			; e = yes, accept as data
	cmp	ah,'?'			; need help?
	jne	evalt2			; ne = no
	call	fcmdhlp			; compute cmhlp to proper string
	push	cmsiz
	mov	cmsiz,0			; so help is with provided text
	call	cmkyhlp
	pop	cmsiz
	jmp	repars
	jmp	evalt1			; keep going

evalt2:	cmp	ah,escape		; ESC?
	jne	evalt2b			; ne = no
	cmp	evaltype,F_contents	; \fcontents(macro)?
	je	evalt2a			; e = yes
	cmp	evaltype,F_definition	; \fdefinition(macro)?
	jne	evalt2b			; ne = no
evalt2a:push	cmptab			; save working pointers
	push	cmsptr
	mov	cmptab,offset mcctab	; look at table of macro names
	mov	bx,cmrptr		; last byte + 1 read
	sub	bx,cmsiz		; look at whole word todate
	mov	cmsptr,bx
	call	cmkyesc			; do escape word completion
	pop	cmsptr
	pop	cmptab
	mov	ah,escape		; in case no match
evalt2b:mov	bx,cmsptr		; where word started
	sub	bx,2			; omit \f too
	mov	cmrptr,bx		; omit word to date
	call	bufreset		; resets cmwptr too
	cmp	ah,escape		; escape?
	jne	evalt3			; ne = no
	push	ax
	push	dx
	mov	ah,conout		; ring the bell
	mov	dl,bell
	int	dos
	pop	dx
	pop	ax
	jmp	evalt1
evalt3:	jmp	repars			; reparse command without \f...

evalt5:	inc	cmsiz			; count argument bytes
	cmp	ah,'('			; brace?
	jne	evalt5a			; ne = no
	inc	temp			; count up
evalt5a:cmp	ah,')'			; terminator?
	jne	evalt1			; ne = no
	dec	temp			; count down braces
	jg	evalt1			; g = still nested, continue
	call	takopen_sub		; open take as text substitution
	jnc	evalt5c			; nc = success
evalt5b:jmp	evalt30			; fail
evalt5c:mov	bx,takadr		; Take structure
	mov	ax,tbufsiz		; bytes of buffer space wanted
	call	malloc
	jc	evalt5b			; c = failed
	mov	[bx].takbuf,ax		; seg of allocated buffer
	or	[bx].takattr,take_malloc ; remember to dispose via takclos
	mov	es,ax
	mov	word ptr es:[0],tbufsiz
	mov	di,[bx].takptr		; where to start writing
	mov	[bx].takcnt,0		; number of unread bytes
	mov	[bx].takper,0
	cmp	evaltype,F_contents	; \fcontents(macro)?
	je	evalt5d			; e = yes
	cmp	evaltype,F_definition	; \fdefinition(macro)?
	je	evalt5d			; e = yes
	cmp	evaltype,F_literal	; \fliteral(string)?
	jne	evalt5e			; ne = no
evalt5d:mov	[bx].takper,1		; treat macro names as literals

evalt5e:mov	ax,valtmp		; where '\fname' started
	sub	ax,2			; back over \f
	mov	cmwptr,ax		; where to write new output
	dec	cmsiz			; uncount trailing ')'
	mov	cx,cmsiz		; length of arg list
	mov	si,cmrptr		; last byte + 1 read
	mov	cmrptr,ax		; readjust read pointer too
	dec	si			; points at trailing ')'
	sub	si,cmsiz		; point at start of arg list
	call	evarg			; parse to argv pointer list
	mov	bx,takadr
	mov	di,[bx].takptr		; destination for replacment text

	cmp	evaltype,F_length	; \flength(text)?
	jne	evalt6			; ne = no
	mov	ax,arglen		; length of variable name
	call	fdec2di			; convert to ASCII string
	mov	bx,takadr
	sub	di,[bx].takptr		; end minus start
	mov	[bx].takcnt,di		; string length
	jmp	evalt30

evalt6:	cmp	evaltype,F_upper	; \fupper(text)?
	je	evalt6a			; e = yes
	cmp	evaltype,F_lower	; \flower(text)?
	jne	evalt7			; ne = no
evalt6a:mov	si,argptr		; point to argument
	mov	cx,arglen		; length of string
	jcxz	evalt6e			; z = empty
	cld
evalt6b:lodsb				; read from parse buffer
	cmp	evaltype,F_lower	; to lower?
	ja	evalt6c			; a = no, upper
	call	ftolowr			; move to lower case
	jmp	short evalt6d
evalt6c:call	toupr			; move to upper case
evalt6d:stosb				; write byte to Take buffer
	loop	evalt6b
evalt6e:mov	bx,takadr
	mov	cx,arglen		; argument length
	mov	[bx].takcnt,cx		; count entered bytes
	jmp	evalt30

evalt7:	cmp	evaltype,F_char		; \fcharacter(n)?
	jne	evalt8			; ne = no
	mov	si,argptr		; point at text of number
	call	evnum			; convert to number
	cld
	stosb				; store single byte
	mov	bx,takadr
	mov	[bx].takcnt,1		; one byte as result
evalt7d:jmp	evalt30

evalt8:	cmp	evaltype,F_substr	; \fsubstr(text, n1, n2)?
	jne	evalt9			; ne = no
	xor	ax,ax
	mov	si,argptr[2]		; point to n1 string
	call	evnum
	jnc	evalt8g			; nc = got a number
evalt8f:xor	ax,ax
	xor	dx,dx
	jmp	short evalt8a
evalt8g:or	dx,dx			; number overflowed to high word?
	jnz	evalt8f			; nz = yes, reset number to zero
	or	ax,ax			; zero?
	jz	evalt8a			; z = yes
	dec	ax			; count from 0 internally
evalt8a:mov	si,argptr		; point at text
	add	si,ax			; point at text[n1]
	mov	cx,arglen		; length of 'text'
	sub	cx,ax			; bytes remaining in 'text'
	jle	evalt8d			; le = nothing left to extract
	push	si
	mov	si,argptr[4]		; point to n2 string
	call	evnum
	pop	si
	jnc	evalt8b			; nc = got a number
	mov	ax,arglen		; default to full string
	xor	dx,dx
evalt8b:or	dx,dx			; number overflowed to high word?
	jnz	evalt8d			; nz = yes
	cmp	cx,ax			; length available more than needed?
	jbe	evalt8c			; be = no
	mov	cx,ax			; use smaller n2
evalt8c:mov	bx,tbufsiz-2		; unused buffer capacity (tbufsiz-2)
	cmp	cx,bx			; n larger than buffer?
	jbe	evalt8e			; be = no
	mov	ax,cx			; remember amount wanted
	call	evmem			; get more memory, want CX bytes
	xchg	ax,cx			; now ax=amount available, cx=wanted
	cmp	cx,ax			; wanted > available?
	jbe	evalt8e			; be = no
	mov	cx,ax			; limit to available memory
evalt8e:mov	bx,takadr
	mov	[bx].takcnt,cx		; count of bytes
	cld
	rep	movsb			; copy bytes to Take buffer
evalt8d:jmp	evalt30

evalt9:	cmp	evaltype,F_right	; \fright(text, n)?
	jne	evalt10			; ne = no
	push	si
	mov	si,argptr[2]		; point to n string
	call	evnum
	pop	si
	jnc	evalt9a			; nc = got a number
	mov	ax,arglen		; default to full string
	xor	dx,dx
evalt9a:or	dx,dx			; number overflowed to high word?
	jnz	evalt9c			; nz = yes
	mov	cx,arglen		; length of 'text'
	cmp	cx,ax			; length available more than needed?
	jbe	evalt9b			; be = no
	mov	cx,ax			; use smaller n
evalt9b:mov	si,argptr		; start of 'text'
	add	si,arglen		; point at last + 1 byte of 'text'
	sub	si,cx			; minus bytes to be copied
	mov	bx,takadr
	mov	[bx].takcnt,cx		; count of bytes
	cld
	rep	movsb			; copy bytes to Take buffer
evalt9c:jmp	evalt30

evalt10:cmp	evaltype,F_literal	; \fliteral(text)?
	jne	evalt11			; ne = no
	mov	cx,cmsiz		; length of text (inc leading spaces)
	mov	bx,takadr
	mov	[bx].takcnt,cx		; count of bytes
	cld
	rep	movsb			; copy bytes to Take buffer
	jmp	evalt30

evalt11:cmp	evaltype,F_rpad		; \frpad(text, n, c)?
	jne	evalt12
	mov	si,argptr+2		; get n
	call	evnum
	jc	evalt11d		; c = error
	or	dx,dx			; numeric overflow to high word?
	jne	evalt11d		; ne = yes
	mov	bx,takadr
	mov	cx,tbufsiz-2		; unused buffer capacity (tbufsiz-2)
	cmp	ax,cx			; n larger than buffer?
	jbe	evalt11a		; be = no
	call	evmem			; get more memory
	jc	evalt11d		; c = fail
	cmp	ax,cx			; enough?
	jbe	evalt11a		; be = yes
	mov	ax,cx
evalt11a:mov	cx,arglen		; length of text
	cmp	ax,cx			; field (n) shorter than text?
	jae	evalt11b		; ae = no, use full text
	mov	cx,ax			; copy just n of text
evalt11b:sub	ax,cx			; available minus used bytes
	mov	bx,takadr
	mov	[bx].takcnt,cx		; count of bytes
	mov	si,argptr		; text
	cld
	rep	movsb			; copy bytes to Take buffer
	mov	cx,ax			; padding bytes
	jcxz	evalt11d		; z = none
	add	[bx].takcnt,cx		; increase count of bytes
	mov	bx,argptr+4		; point to c
	mov	al,[bx]			; padding character, if any
	cmp	arglen+4,0		; any char given?
	jne	evalt11c		; ne = yes
	mov	al,' '			; else default to space
evalt11c:cld
	rep	stosb			; append byte to Take buffer
evalt11d:jmp	evalt30

evalt12:cmp	evaltype,F_lpad		; \flpad(text, n, c)?
	jne	evalt13
	push	si
	mov	si,argptr+2		; get n
	call	evnum
	pop	si
	jc	evalt12e		; c = error
	or	dx,dx			; numeric overflow to high word?
	jne	evalt12e		; ne = yes
	mov	bx,takadr
	mov	cx,tbufsiz-2		; unused buffer capacity (tbufsiz-2)
	cmp	ax,cx			; n larger than buffer?
	jbe	evalt12a		; be = no
	call	evmem			; get more memory
	cmp	ax,cx			; wanted > available?
	jbe	evalt12a		; be = no
	mov	ax,cx			; limit text
evalt12a:sub	ax,arglen		; n - length of 'text'
	jns	evalt12b		; ns = no underflow
	xor	ax,ax			; else omit padding
evalt12b:mov	cx,ax			; padding count
	mov	[bx].takcnt,cx		; count of bytes
	mov	bx,argptr+4		; point to c
	mov	al,[bx]			; padding character, if any
	cmp	arglen+4,0		; any char given?
	jne	evalt12c		; ne = yes
	mov	al,' '			; default to space
evalt12c:cld
	rep	stosb			; copy byte to Take buffer
	mov	cx,arglen		; length of text
	mov	cx,tbufsiz-2		; buffer capacity
	mov	bx,takadr
	sub	cx,[bx].takcnt		; minus bytes written so far
	mov	ax,arglen		; length of 'text'
	cmp	ax,cx			; n larger than buffer?
	jbe	evalt12d		; be = no
	mov	ax,cx
evalt12d:mov	cx,ax			; append count
	mov	bx,takadr
	add	[bx].takcnt,cx		; increase count of bytes
	mov	si,argptr		; start of text
	cld
	rep	movsb			; append byte to Take buffer
evalt12e:jmp	evalt30

evalt13:cmp	evaltype,F_code		; \fcode(char)?
	jne	evalt14			; ne = no
	mov	bx,argptr		; point to char address
	mov	cx,arglen		; length of string
	mov	al,[bx]			; char (defaults to null)
	xor	ah,ah
	cmp	al,'\'			; possible \number ?
	jne	evalt13a		; ne = no
	cmp	cx,1			; more than just '\'?
	je	evalt13a		; e = no
	call	fkatoi			; convert to binary in dx:ax
evalt13a:call	fdec2di			; convert to ASCII string, no '\'
	mov	bx,takadr		;   prefix
	sub	di,[bx].takptr		; end minus start
	mov	[bx].takcnt,di		; string length
	jmp	evalt30

evalt14:cmp	evaltype,F_definition	; \fdefinition(macro)?
	je	evalt14i		; e = yes
	cmp	evaltype,F_contents	; \fcontents(macro)
	jne	evalt15
evalt14i:cmp	arglen,0		; any name?
	jne	evalt14a		; e = yes
	jmp	evalt14x		; fail
evalt14a:mov	si,argptr		; trim trailing spaces from user name
	mov	cx,arglen		; length of raw user string
	mov	bx,si
	add	bx,cx
evalt14j:cmp	byte ptr [bx-1],' '	; trailing space?
	jne	evalt14k		; ne = no
	dec	arglen			; remove it
	dec	bx			; preceeding char
	loop	evalt14j
evalt14k:
	mov	si,offset mcctab	; table of macro names
	cld
	lodsb
	mov	cl,al			; number of macro entries
	xor	ch,ch
	jcxz	evalt14x		; z = none
evalt14b:push	cx			; save loop counter
	lodsw				; length of macro name
	mov	cx,arglen		; length of user's string
	cmp	ax,cx			; mac name shorter that user spec?
	jb	evalt14d		; b = yes, no match
	push	ax
	push	si			; save these around match test
	push	di
	mov	di,argptr		; user's string
evalt14c:mov	ah,[di]
	inc	di
	lodsb				; al = mac name char, ah = user char
	and	ax,not 2020h		; clear bits (uppercase chars)
	cmp	ah,al			; same?
	loope	evalt14c		; while equal, do more
	pop	di
	pop	si			; restore regs
	pop	ax
	je	evalt14e		; e = a match
evalt14d:add	si,ax			; point to next name, add name length
	add	si,2			;  and string pointer
	pop	cx			; recover loop counter
	loop	evalt14b		; one less macro to examine
evalt14x:jmp	evalt30

evalt14e:pop	cx			; pop loop counter above
	cmp	byte ptr[si],0		; name starts with null char?
	jne	evalt14f		; ne = no
	jmp	evalt30			; yes, TAKE file, ignore
evalt14f:mov	ax,[si-2]		; length of macro name
	add	si,ax			; skip over name
	push	es
	mov	es,[si]			; segment of string structure
	xor	si,si			; es:si = address of count + string
	mov	ax,es
	mov	cx,es:[si]		; length of string
	mov	arglen+2,cx		; save length here
	mov	argptr+2,ax		; save es of es:si here
	pop	es
	call	evmem			; get new memory, set new ES:DI
	cmp	cx,arglen+2		; enough to hold material?
	jae	evalt14h		; ae = yes
	mov	arglen+2,cx		; shrink string size too

evalt14h:push	ds
	mov	bx,takadr
	mov	cx,arglen+2		; length of string
	mov	[bx].takcnt,cx		; macro length
mov ax,arglen+4
add [bx].takcnt,ax
	mov	[bx].takptr,2		; offset of text in destination
	mov	ax,argptr+2		; seg of string
	mov	ds,ax
	mov	si,2			; offset of string
	cld
	rep	movsb			; copy string
	pop	ds
	jmp	evalt30

evalt15:cmp	evaltype,F_maximum	; \fmaximum(n1, n2)?
	je	evalt15a		; e = yes
	cmp	evaltype,F_minimum	; \fminimum(n1, n2)?
	jne	evalt16			; ne = no
evalt15a:push	si
	mov	si,argptr[0]		; point to n1 string
	call	evnum
	pop	si
	or	dx,dx			; number overflowed to high word?
	jnz	evalt15x		; nz = yes
	mov	temp,ax			; save n1
	push	si
	mov	si,argptr[2]		; point to n2 string
	call	evnum
	pop	si
	or	dx,dx			; number overflowed to high word?
	jnz	evalt15x		; nz = yes
	cmp	temp,ax			; n1 > n2?
	jbe	evalt15d		; be = no, n2 is max
	xchg	temp,ax			; put larger number in ax
evalt15d:cmp	evaltype,F_maximum	; return max of the pair?
	je	evalt15e		; e = yes
	xchg	temp,ax			; put selected number in ax
evalt15e:call	fdec2di			; convert to ASCII string
	mov	bx,takadr
	sub	di,[bx].takptr		; bytes consumed
	mov	[bx].takcnt,di
evalt15x:jmp	evalt30

evalt16:cmp	evaltype,F_index	; \findex(pat, string, [offset])
	jne	evalt17
	cmp	arglen+4,0		; optional offset given?
	je	evalt16a		; e = no, use userlevel 1
	push	si
	mov	si,argptr+4		; evaluate optional offset
	call	evnum			; to binary in dx:ax
	pop	si
	jnc	evalt16b		; nc = got a number
evalt16a:mov	ax,1			; default to one (user style)
	xor	dx,dx
evalt16b:or	dx,dx			; too large?
	jnz	evalt16x		; nz = too large, fail
	dec	ax			; offset from start (0 base it)
	mov	temp,ax			; remember offset
	push	es
	push	di
	mov	di,ds			; string is in DS seg
	mov	es,di
	mov	cx,arglen+2		; length of string
	sub	cx,arglen		; minus length of pattern
	cmp	ax,cx			; offset larger than this?
	jbe	evalt16c		; be = no
	mov	temp,-1			; to report zero
	jmp	short evalt16e		;  fail
evalt16c:mov	si,argptr		; pattern
	mov	di,argptr+2		; string
	add	di,temp			; plus offset
	mov	cx,arglen		; pattern length
	mov	ax,cx
	add	ax,temp			; plus offset
	cmp	ax,arglen+2		; more than string?
	jbe	evalt16d		; be = no, keep going
	mov	temp,-1			; to report zero
	jmp	short evalt16e		; a = no match
evalt16d:push	cx
	cld
	repe	cmpsb			; compare strings
	pop	cx
	je	evalt16e		; e = matched
	inc	temp			; move pattern right one place
	jmp	short evalt16c		; try again
evalt16e:pop	di
	pop	es
	mov	ax,temp			; report out number
	inc	ax			; show 0 as 1 to user
	mov	bx,takadr
	call	fdec2di			; binary to ASCIIZ
	mov	bx,takadr
	sub	di,[bx].takptr
	mov	[bx].takcnt,di		; length of ASCIIZ result
evalt16x:jmp	evalt30

evalt17:cmp	evaltype,F_repeat	; \frepeat(text,n)?
	jne	evalt18			; ne = no
	cmp	arglen+2,0		; number given?
	je	evalt17g		; e = no, fail
	mov	si,argptr+2		; n
	call	evnum			; convert to binary dx:ax
	jc	evalt17g		; c = fail
	or	dx,dx			; number overflowed?
	jz	evalt17a		; z = no
	mov	ax,1			; default number
evalt17a:mov	arglen+2,ax		; save reasonable value here
	mul	arglen			; string length * repeats
	or	dx,dx			; overflowed (> 64KB)?
	jnz	evalt17b		; nz = yes
	cmp	ax,cmdblen		; more than parse buffer?
	jbe	evalt17c		; be = no
evalt17b:mov	ax,cmdblen		; should be plenty (1000)
evalt17c:mov	cx,ax			; bytes wanted
	call	evmem			; allocate memory 
	mov	dx,cx			; buffer length available
evalt17d:mov	cx,arglen		; length of text
	cmp	cx,dx			; length vs buffer capacity
	jbe	evalt17e		; be = capacity exceeds string
	mov	cx,dx			; chop string to fit
evalt17e:sub	dx,cx			; deduct amount done
	mov	si,argptr		; point at string
	cld
	rep	movsb
	dec	arglen+2		; repeat count
	cmp	arglen+2,0		; any more repeats to do?
	je	evalt17f		; e = no
	or	dx,dx			; any space left?
	jg	evalt17d		; g = yes
evalt17f:mov	bx,takadr
	sub	di,[bx].takptr		; end minus start
	mov	[bx].takcnt,di		; string length
evalt17g:jmp	evalt30

evalt18:cmp	evaltype,F_reverse	; \freverse(text)?
	jne	evalt19			; ne = no
	mov	bx,takadr
	mov	ax,arglen		; length of string
	mov	[bx].takcnt,ax		; length of output to buffer
	mov	si,argptr		; string
	add	di,ax
	dec	di			; last byte in output string
	mov	cx,ax			; bytes in string
	jcxz	evalt18b		; z = none
evalt18a:cld
	lodsb				; read left
	std
	stosb				; write left, displaced, backward
	cld
	loop	evalt18a
evalt18b:jmp	evalt30

evalt19:cmp	evaltype,F_date		; \fdate(filename)?
	je	evalt19a		; e = yes
	cmp	evaltype,F_size		; \fsize(filename)?
	jne	evalt20			; ne = no
evalt19a:push	di
	mov	di,offset tmpbuf	; work buffer in this data seg
	mov	byte ptr [di],0		; terminator
	call	filedate		; get info
	mov	dx,offset tmpbuf
	call	fstrlen			; get length of results to CX
	pop	di
	mov	bx,takadr
	mov	[bx].takcnt,cx		; length of results
	mov	si,offset tmpbuf
	cld
	rep	movsb			; copy to Take buffer
	jmp	evalt30

evalt20:cmp	evaltype,F_files	; \ffiles(filespec)?
	jne	evalt21			; ne = no
	mov	temp,0			; count found files
	call	filedate		; find first
	jc	evalt20d		; c = none
	push	di
	push	es
	inc	temp			; count found file
	mov	dx,offset fileio.dta	; point at dta
	mov	ah,setdma		; set the dta address
	int	dos
evalt20b:mov	ah,next2		; DOS 2.0 search for next
	int	dos
	jc	evalt20c		; c = no more
	inc	temp			; count found file
	jmp	short evalt20b		; try for next
evalt20c:mov	ah,setdma		; restore dta
	mov	dx,offset buff
	int	dos
	pop	es
	pop	di
	call	filedate		; go back to find first again
evalt20d:mov	ax,temp			; count of files found
	call	fdec2di			; convert to ASCII string
	mov	bx,takadr
	sub	di,[bx].takptr		; end minus start
	mov	[bx].takcnt,di		; string length
	jmp	evalt30

evalt21:cmp	evaltype,F_nextfile	; \fnextfile()?
	jne	evalt24			; ne = no
	mov	si,offset fileio.fname	; current filename from DOS
	mov	dx,si
	call	fstrlen			; length of name
	mov	bx,takadr
	mov	[bx].takcnt,cx		; length of result
	rep	movsb			; copy to Take buffer
	push	di
	push	es
	mov	dx,offset fileio.dta	; point at dta
	mov	ah,setdma		; set the dta address
	int	dos
	mov	ah,next2		; DOS 2.0 search for next
	int	dos
	mov	ah,setdma		; restore dta
	mov	dx,offset buff
	int	dos
	pop	es
	pop	di
evalt21b:jmp	evalt30

evalt24:cmp	evaltype,F_replace	; \freplace(source,pat,replacement)?
	jne	evalt30			; ne = no
	cmp	arglen+2,0		; arg2 omitted?
	jne	evalt24a		; ne = no
	mov	cx,arglen		; source length
	mov	si,argptr		; source
	jmp	evalt24g		; copy source to output

evalt24a:push	es			; provided Take buffer, save
	push	di
	mov	ax,seg decbuf		; use local temp buf
	mov	es,ax
	mov	di,offset decbuf	; as es:di
	mov	si,argptr		; make source ds:si
	mov	cx,arglen		; length of source
	cld
evalt24b:push	cx			; save source counter
	push	si			; save source pointer
	cmp	cx,arglen+2		; fewer source bytes than pattern?
	jb	evalt24e		; b = yes, no replacement possible
	mov	bx,argptr+2		; pattern
	mov	cx,arglen+2		; length of pattern

evalt24c:mov	ah,[bx]			; pattern byte
	cmp	[si],ah			; same as source byte?
	jne	evalt24e		; ne = no, no match
	inc	si			; next bytes to match
	inc	bx
	loop	evalt24c		; do all pattern bytes
	mov	si,argptr+4		; get replacement pattern
	mov	cx,arglen+4		; its length
	mov	ax,di			; starting offset in temp buffer
	sub	ax,offset decbuf	; minus start of buffer
	add	ax,cx			; count of bytes to be in buffer
	cmp	ax,decbuflen		; longer than buffer?
	jbe	evalt24d		; be = no overflow
	sub	ax,cx			; get back bytes already in buffer
	sub	ax,decbuflen
	neg	ax			; bytes available
	mov	cx,ax			; move just that many
evalt24d:rep	movsb			; copy to output (may be nothing)
	pop	si
	pop	cx
	add	si,arglen+2		; length of pattern
	sub	cx,arglen+2		; bytes left in source
	jmp	short evalt24f

evalt24e:pop	si			; mismatch comes here
	pop	cx
	movsb				; write source byte to output buffer
	dec	cx			; one less source byte
evalt24f:or	cx,cx			; qty of source bytes remaining
	jg	evalt24b		; g = more source bytes to examine
	sub	di,offset decbuf	; length of output material
	mov	cx,di
	mov	ax,cx			; save length in ax
	pop	di
	pop	es			; original Take buffer
	call	evmem			; allocate new Take buffer, len cx
	mov	si,offset decbuf
	mov	cx,ax			; length used in decbuf
evalt24g:cld
	rep	movsb			; copy decbuf to Take buffer
	mov	bx,takadr
	sub	di,[bx].takptr
	mov	[bx].takcnt,di		; length of ASCIIZ result
	jmp	short evalt30

evalt30:pop	temp
	pop	si
	pop	dx			; trailing space flag
	clc				; success or fail, consume source
	ret
evaltoa	endp

; Worker for evaltoa. Given ds:si at start of non-whitespace text, convert
; text to number in dx:ax.
; Return, if success carry clear, dx:ax holding number.
; Return, if fail carry clear.
; Register SI is preserved
evnum	proc	near
	or	si,si			; valid pointer?
	jz	evnumx			; z = no
	push	si
	cmp	byte ptr [si],'\'	; number slash present?
	je	evnum1			; e = yes
	dec	si			; backup to just before item
	mov	byte ptr [si],'\'	; overwrite source, add '\'
evnum1:	call	fkatoi			; convert to value in dx:ax
	pop	si
	jc	evnumx			; c = failed to find a number
	clc				; success, number is in dx:ax
	ret
evnumx:	xor	ax,ax
	xor	dx,dx
	stc				; failure
	ret
evnum	endp

; Worker for evaltoa. 
; Given start of string in DS:SI and string length in CMSIZ.
; Return pointers to up to three arguments (as offset of start of their
; non-white space) and the lengths of each argument field. Arrays argptr
; and arglen hold these 16 bit values.
; Bare commas separate arguments, curly braces and parentheses protect commas,
; curly braces protect strings, starting with a curly brace is considered to 
; mean use material within braces. A closing curly brace closes parens.
evarg	proc	near
	push	ax
	push	bx
	push	cx
	push	dx
	push	si
	push	temp
	xor	ax,ax
	mov	argptr,ax		; clear arg pointer list
	mov	argptr+2,ax
	mov	argptr+4,ax
	mov	arglen,ax		; clear arg length list
	mov	arglen+2,ax
	mov	arglen+4,ax
	mov	cx,cmsiz		; length of arg list
	xor	bx,bx			; arg counter
	xor	dx,dx			; curly brace counters
	mov	temp,0			; paren counter
	cld
	or	cx,cx
	jg	evarg1			; g = something to read
	jmp	evarg7			; empty string
evarg1:	lodsb				; read a byte
	cmp	al,' '			; leading white space?
	loope	evarg1			; e = yes, trim off
	dec	si
	inc	cx			; non-white char
	cmp	al,braceop		; starting on an opening curly brace?
	jne	evarg1a			; ne = no
	mov	dx,101h			; remember braces delimit string
	inc	si			; skip brace
	dec	cx			; don't count in length
evarg1a:mov	argptr[bx],si		; pointer to argument

evarg2:	lodsb				; read byte
	cmp	al,'('			; opening paren?
	jne	evarg2a			; ne = no
	inc	temp			; count up paren
	jmp	evarg4			; did this one
evarg2a:cmp	al,')'			; closing paren?
	jne	evarg2b			; ne = no
	sub	temp,1
	jns	evarg4			; ns = no underflow
 	mov	temp,0			; clamp at zero
	jmp	evarg4

evarg2b:cmp	al,braceop		; opening curly brace?
	jne	evarg3			; ne = no
	inc	dl			; count up curly brace
	jmp	short evarg4
evarg3:	cmp	al,bracecl		; closing curly brace?
	jne	evarg4			; ne = no
	dec	dl			; count down curly brace
	or	dl,dl			; underflow?
	jns	evarg3a			; ns = no underflow
	xor	dl,dl			; clamp to zero
evarg3a:or	dl,dl			; last curly brace?
	jnz	evarg3b			; nz = no
	mov	temp,0			; yes, terminates parens too
evarg3b:cmp	dh,1			; did string start on curly brace?
	jne	evarg4			; ne = no
	push	si
	dec	si			; don't count ending brace
	sub	si,argptr[bx]
	mov	arglen[bx],si		; string length (within braces)
	pop	si
	mov	dh,2			; remember have computed length
evarg4:	or	dl,dl			; within curly braces?
	jne	evarg5			; ne = yes, treat commas as data
	cmp	temp,0			; within parens?
	jne	evarg5			; ne = yes, treat commas as data
	cmp	al,','			; possible separator?
evarg5:	loopne	evarg2			; ne = no, keep scanning
	mov	ax,si
	jne	evarg6			; ne = ran out of counts
	dec	ax			; back up to separator
evarg6:	cmp	dh,2			; have computed length already?
	je	evarg6a			; e = yes
	sub	ax,argptr[bx]
	mov	arglen[bx],ax		; string length (within braces)
evarg6a:add	bx,2			; next arg array
	xor	dx,dx			; reset brace counters
	cmp	bx,3*2			; done three args?
	jae	evarg7			; a = yes, quit
	or	cx,cx			; any bytes left?
	jnz	evarg1			; nz = yes, keep going
evarg7:	pop	temp
	pop	si
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	clc
	ret
evarg	endp

; Reallocates more memory than provided in the default takopen 128 byte buffer.
; Enter with desired byte quantity in CX and a Take structure already
; present at takadr. Returns with new buffer length in CX, ES:DI pointing
; at buffer+2 ([bx].takbuf:[bx].takptr)
evmem	proc	near
	push	ax
	push	bx
	push	cx
	mov	bx,takadr
	mov	ax,[bx].takbuf		; segment of preallocated memory
	mov	bx,cx			; string length, in bytes
	add	bx,2+1+15		; count + null term + round up
	mov	cl,4
	shr	bx,cl			; convert to paragraphs (divide by 16)
	mov	cx,bx			; remember desired paragraphs
	mov	ah,alloc		; allocate a memory block
	int	dos			; ax=seg, bx=paragraphs
	jc	evmem3			; c = error, not enough memory
 	cmp	bx,cx			; obtained vs wanted
	jae	evmem2			; ae = enough
	push	bx
	mov	bx,takadr
	mov	cx,[bx].takcnt		; old unread-bytes
	shr	cx,1
	shr	cx,1
	shr	cx,1
	shr	cx,1			; to paragraphs
	pop	bx
	cmp	bx,cx			; more than we had originally?
	ja	evmem2			; a = yes, so use the new larger buff
	push	es
	mov	es,ax			; new allocated segment
	mov	ah,freemem		; free it
	int	dos
	pop	es
	jmp	short evmem3		; use original segment

evmem2:	mov	cl,4
	shl	bx,cl			; paragraphs to bytes
	mov	cx,bx			; return new bytes in CX
	sub	cx,2			; minus two for len field
	mov	es,ax			; set new segment
	mov	bx,takadr
	xchg	[bx].takbuf,ax		; new segment, swap with old seg
	or	ax,ax			; if any allocated
	jz	evmem2a			; z = none
	push	es
	mov	es,ax			; old allocated segment
	mov	ah,freemem		; free old segment
	int	dos
	pop	es
evmem2a:mov	di,2			; new offset
	mov	bx,takadr
	mov	[bx].takptr,di		; new offset
	mov	[bx].takcnt,0		; total buffer, unread bytes
	or	[bx].takattr,take_malloc ; malloc'd buffer to remove later
	pop	bx			; discard old cx, return new cx
	pop	bx
	pop	ax
	ret

evmem3:	pop	cx			; return original values
	pop	bx
	pop	ax
	ret
evmem	endp

; Worker for evaltoa. Sets cmhlp to help string matching evaltype
fcmdhlp	proc	near
	mov	ax,evaltype		; kind of \f command
	mov	bx,offset f1hlp		; number
	cmp	ax,F_char		; go through types
	je	fcmdh1			; e = matched
	cmp	ax,F_maximum
	je	fcmdh1
	cmp	ax,F_minimum
	je	fcmdh1
	mov	bx,offset f2hlp		; char
	cmp	ax,F_code
	je	fcmdh1
	mov	bx,offset f3hlp
	cmp	ax,F_contents
	je	fcmdh1
	cmp	ax,F_definition
	je	fcmdh1
	mov	bx,offset f8hlp
	cmp	ax,F_index
	je	fcmdh1
	mov	bx,offset f12hlp
	cmp	ax,F_lpad
	je	fcmdh1
	cmp	ax,F_rpad
	je	fcmdh1
	mov	bx,offset f15hlp
	cmp	ax,F_nextfile
	je	fcmdh1
	mov	bx,offset f16hlp
	cmp	ax,F_repeat
	je	fcmdh1
	mov	bx,offset f18hlp
	cmp	ax,F_right
	je	fcmdh1
	mov	bx,offset f20hlp
	cmp	ax,F_substr
	je	fcmdh1
	mov	bx,offset f22hlp
	cmp	ax,F_date
	je	fcmdh1
	cmp	ax,F_size
	je	fcmdh1
	cmp	ax,F_files
	je	fcmdh1
	mov	bx,offset f9hlp		; default to text
fcmdh1:	mov	cmhlp,bx		; set help text pointer
	ret
fcmdhlp	endp


filedate proc	near
	push	di
	push	es
	mov	dx,offset fileio.dta	; data transfer address
	mov	ah,setdma		; set disk transfer address
	int	dos
	mov	bx,argptr		; filename
	mov	dx,bx			; for file open
	add	bx,arglen		; length of it
	mov	byte ptr [bx],0		; make it ASCIIZ
	xor	cx,cx			; attributes: find only normal files
	mov	ah,first2		; DOS 2.0 search for first
	int	dos			; get file's characteristics
	pushf				; save status
	mov	ah,setdma		; restore dta
	mov	dx,offset buff
	int	dos
	popf				; get status
	pop	es
	pop	di
	jnc	filed1			; nc = success
	ret				; fail
filed1:	cmp	evaltype,F_date		; want date/time stamp?
	je	filed3			; e = yes
	cmp	evaltype,F_size		; want file length?
	jne	filed2			; ne = no (probably \ffiles(..))
	mov	ax,fileio.sizelo	; file size to dx:ax
	mov	dx,fileio.sizehi
	call	flnout			; convert to ASCII
	mov	byte ptr [di],0		; terminate
filed2:	clc
	ret

filed3:	mov	bx,offset fileio.dta	; set work pointer
	call	filetdate		; do the work
	ret
filedate endp

; Write file date and timestamp to ds:di, given DTA in ds:bx.
filetdate proc	far
	push	es			; time/date stamp yyyymmdd hh:mm:ss
	mov	ax,ds
	mov	es,ax
	xor	ah,ah
	mov	al,[bx+25]		; yyyyyyym from DOS via file open
	shr	al,1			; get year
	add	ax,1980			; add bias
	xor	dx,dx
	call	flnout			; put year (1990) in buffer
	mov	ax,[bx+24]		; yyyyyyyym mmmddddd  year+month+day
	shr	ax,1			; month to al
	xor	ah,ah
	mov	cl,4
	shr	al,cl			; month to low nibble
	mov	byte ptr[di],'0'	; leading digit
	inc	di
	cmp	al,9			; more than one digit?
	jbe	filed4			; be = no
	mov	byte ptr[di-1],'1'	; new leading digit
	sub	al,10			; get remainder
filed4:	add	al,'0'			; to ascii
	stosb				; end of month
	mov	al,[bx+24]		; get day of month
	and	al,1fh			; select day bits
	xor	ah,ah
	mov	cl,10
	div	cl			; quot = al, rem = ah
	add	ax,'00'			; add ascii bias
	stosw				; leading digit and end of date
	mov	al,' '			; space separator
	stosb
	mov	al,[bx+23]		; hours  hhhhhmmm
	mov	cl,3
	shr	al,cl			; move to low nibble
	xor	ah,ah
	mov	cl,10
	div	cl			; quot = al, rem = ah
	add	ax,'00'			; add ascii bias
	stosw				; store hours
	mov	al,':'			; separator
	stosb
	mov	ax,[bx+22]		; get minutes: hhhhhmmm mmmsssss
	mov	cl,5
	shr	ax,cl			; minutes to low byte
	and	al,3fh			; six bits for minutes
	xor	ah,ah
	mov	cl,10
	div	cl
	add	ax,'00'			; add ascii bias
	stosw
	mov	al,':'			; separator
	stosb
	mov	al,[bx+22]		; get seconds (double secs really)
	and	al,1fh
	shl	al,1			; DOS counts by two sec increments
	xor	ah,ah
	mov	cl,10
	div	cl
	add	ax,'00'			; add ascii bias
	stosw
	mov	byte ptr [di],0		; terminate
	pop	es
	ret
filetdate endp

; Set envadr to the string following the <variable=> keyword in the DOS
; Environment and set envlen to its length after removing leading and
; trailing whitespace.
; <variable> starts at ds:<valtmp>, of length cmsiz-1, and can be mixed case.
; Return carry set if can't find the <variable=> line in the Environment.
envvar	proc	near
	push	es
	mov	bx,valtmp		; start of variable name
	mov	cx,cmsiz		; length of variable name, w/o ')'
	or	cx,cx			; empty?
	jle	envvar3			; le = nothing to look for, fail
	push	bx
	push	cx
envvar1:mov	al,byte ptr [bx]	; scan variable name in our buffer
	cmp	al,'a'			; lower case
	jb	envvar2			; b = no
	cmp	al,'z'			; still in lower case range?
	ja	envvar2			; a = no
	and	al,not 20h		; convert to DOS's upper case
	mov	byte ptr [bx],al	; replace char
envvar2:inc	bx
	loop	envvar1
	pop	cx
	pop	bx			; find "<variable>=" in Environment
	call	fgetenv			; dx = offset in Environment of "="
	jnc	envvar4			; nc = success
envvar3:pop	es			; no such variable
	stc				; c = failure
	ret
; dx has offset in Environment of char "="
; ds:valtmp is start of variable name, cmsiz is length + 1 of the name.
; Return seg:offset and length variables so we can copy from there in valtoa
envvar4:push	di
	push	si
	xor	ax,ax
	mov	word ptr envadr,ax	; offset in env of variable's string
	mov	word ptr envadr+2,ax	; seg of same
	mov	envlen,ax		; length of string
	mov	es,psp			; our Prog Seg Prefix segment
	mov	ax,es:word ptr[env]	; pick up Environment address
	mov	es,ax			; set es: to Environment segment
	mov	di,dx			; line scan pointer
	cmp	byte ptr es:[di],'='	; did we stop on this?
	jne	envvar5			; ne = no
	inc	di			; skip the "=" char
envvar5:mov	al,es:[di]		; scan over leading white space
	inc	di
	or	al,al			; end of line terminator?
	jz	envvarf			; z = yes, fail
	cmp	al,TAB			; HT?
	jne	envvar6			; ne = no
	mov	al,' '			; HT becomes a space
envvar6:cmp	al,' '			; white space?
	je	envvar5			; scan off white space
	dec	di			; backup to non-white char
	mov	word ptr envadr,di	; offset of string in Environment
	mov	word ptr envadr+2,es	; seg of string in Environment
	mov	si,di			; remember starting offset here
					; remove trailing spaces from string
	xor	al,al			; a null
	mov	cx,127			; max length to search
	cld
	repne	scasb			; skip over non-nulls
	dec	di			; backup to null
	dec	di			; backup to last string char
	mov	cx,di			; ending offset
	inc	cx			; count the char
	sub	cx,si			; minus starting offset yields length
	jcxz	envvar9			; z = empty string
envvar7:mov	al,es:[di]		; last char
	dec	di			; backup one char
	cmp	al,' '			; space?
	je	envvar8			; e = yes
	cmp	al,TAB			; HT?
	jne	envvar9			; ne = no, end of white space
envvar8:loop	envvar7			; keep looking
envvar9:mov	envlen,cx		; store the length
	clc				; say success
	jmp	short envvarx
envvarf:stc				; say failure
envvarx:pop	si
	pop	di
	pop	es
	ret
envvar	endp

; Worker for Remote Query System name in mssser.
; Enter with variable name in decbuf+6 et seq, name length byte in decbuf+5
; with ' ' bias.
fqryenv	proc	far
	mov	valtmp,offset decbuf+6	; start of variable name
	mov	cl,decbuf+5		; name length plus bias
	sub	cl,' '			; remove bias
	xor	ch,ch
	mov	cmsiz,cx		; name length
	call	envvar			; get address of environmental string
	jnc	fqryenv1		; nc = success
	xor	cx,cx			; failure
	ret

fqryenv1:mov	cx,envlen		; length of string in environment
	push	es
	push	ds
	lds	si,envadr		; pointer to string
	mov	di,seg encbuf		; destination buffer
	mov	es,di
	mov	di,offset encbuf
	cld
	rep	movsb			; copy string
	pop	ds
	pop	es
	mov	cx,envlen		; string length again
	ret
fqryenv	endp

; Read chars from Take file, keyboard, or redirected stdin. Edit and remove
; BS & DEL, Tab becomes space, act on Control-C, pass Control-U and Control-W.
; Do echoing unless comand.cmquiet is non-zero. Do semicolon comments in Take
; and indirect stdin files (\; means literal semicolon). Return char in AL.
CMGETC	proc	near			; Basic raw character reader
	mov	read_source,0		; assume direct reading
	mov	endedbackslash,0	; clear end of sub scan flag
cmget01:cmp	comand.cmdirect,0	; read directly?
	jne	cmget02			; ne = yes
	cmp	taklev,0		; in a Take file?
	jne	cmget1			; ne = yes, do Take reader section
cmget02:call	fisdev			; is stdin a device or a file?
	jnc	cmget20			; nc = file (redirection of stdin)
	jmp	cmget10			; c = device, do separately

cmget20:call	fiseof			; see if file is empty
	jc	cmget21			; c = EOF on disk file
	mov	ah,coninq		; read the char from file, not device
	int	dos
	cmp	al,cr			; is it a cr?
	je	cmget01			; yes, ignore and read next char
	cmp	al,ctlz			; Control-Z?
	je	cmget21			; e = yes, same as EOF here
	cmp	al,lf			; LF's end lines from disk files
	jne	cmget12			; ne = not LF, pass along as is
	mov	al,cr			; make LF a CR for this parser
	call	fiseof			; see if this is the last char in file
	jnc	cmget12			; nc = not EOF, process new CR
cmget21:mov	flags.extflg,1		; EOF on disk file, set exit flag
	jmp	cmget12			; do echoing and return

cmget1:	push	bx			; read from Take file
	mov	bx,takadr		; offset of this Take structure
	mov	al,[bx].taktyp
	mov	read_source,al
	cmp	[bx].takcnt,0		; bytes remaining in Take buffer
	jne	cmget4			; ne = not empty
	cmp	al,take_file		; type of Take (file?)
	jne	cmget3			; ne = no (macro)
	call	ftakrd			; read another buffer
	cmp	[bx].takcnt,0		; anything in the buffer?
	jne	cmget4			; ne = yes
cmget3:	pop	bx			; clear stack
	jmp	short cmget5		; close the Take file

cmget4:	push	si			; read from Take non-empty buffer
	push	es
	mov	es,[bx].takbuf		; segment of Take buffer
	mov	si,[bx].takptr		; current offset in Take buffer
	mov	al,es:[si]		; read a char from Take buffer
	pop	es
	inc	si
	mov	[bx].takptr,si		; move buffer pointer
	pop	si
	dec	[bx].takcnt		; decrease number of bytes remaining
	cmp	read_source,take_sub	; substitution macro?
	jne	cmget4b			; ne = no
	cmp	[bx].takcnt,0		; read last byte?
	jne	cmget4b			; ne = no
	cmp	al,'\'			; ended on a backsash?
	jne	cmget4b			; ne = no
	mov	endedbackslash,al	; setup break end of sub scan
cmget4b:
	cmp	read_source,take_file	; kind of Take/macro, file?
	pop	bx
	jne	cmget12			; ne = no, not a file
	cmp	al,LF			; LF?
	je	cmget01			; e = yes, ignore
	cmp	al,ctlz			; Control-Z?
	jne	cmget12			; ne = no, else ^Z = EOF

cmget5:	push	bx			; end of file on Take buffer
	mov	bx,takadr		; offset of this Take structure
	mov	ah,[bx].takattr		; save kind of autoCR
	pop	bx
	cmp	read_source,take_sub	; text subsititution?
	je	cmget5a			; e = yes, cannot keep open
	cmp	comand.cmkeep,0		; keep Take/macro open after eof?
	jne	cmget5b			; ne = yes
cmget5a:push	ax
	call	ftakclos		; close take file
	pop	ax
	test	ah,take_autocr		; add CR on EOF?
	jnz	cmget5b			; nz = yes
	jmp	cmgetc			; internal macros have no auto CR
cmget5b:mov	al,CR			; report CR as last char
	jmp	short cmget12
					; read from tty device
cmget10:mov	ah,coninq		; Get a char from device, not file
	int	dos			;  with no echoing
	or	al,al
	jnz	cmget11			; ignore null bytes of special keys
	int	dos			; read and discard scan code byte
	jmp	short cmget10		; try again
cmget11:cmp	al,LF			; LF?
	jne	cmget12			; ne = no
	mov	al,CR			; replace interactive LF with CR

cmget12:cmp	al,'C'and 1Fh		; Control-C?
	je	cmget14			; e = yes
	cmp	al,TAB			; tab is replaced by space
	jne	cmget13			; ne = not tab
	mov	al,' '
cmget13:ret				; normal exit, char is in AL

cmget14:cmp	in_showprompt,0		; Non-zero when in showprompt
	jne	cmget13			; ne = yes, no editing
	mov	ah,prstr		; Control-C handler
	push	dx
	mov	dx,offset ctcmsg	; show Control-C
	int	dos
	pop	dx
	mov	flags.cxzflg,'C'	; tell others the news
	jmp	cmexit			; fail immediately via longjmp
cmgetc	endp

; Read chars from user (cmgetc). Detect terminators. Reads from buffer
; cmbuf. Set read pointer cmrptr to next free buffer byte if
; char is not a terminator: chars CR, LF, FF, '?' (returns carry set for
; terminators). Do ^U, ^W editing, convert FF to CR plus clear screen.
; Edit "-<cr>" as line continuation, "\-<cr>" as "-<end of line>".
; Return char in AH.
CMINBF	proc	near			; Buffer reader, final editor
	cmp	cmwptr,offset cmdbuf+size cmdbuf-3 ; max buffer size - 3
	jb	cminb1			; b = not full for writing
	mov	ah,conout		; almost full, notify user
	push	dx
	mov	dl,bell
	int	dos
	pop	dx
	cmp	cmrptr,offset cmdbuf+size cmdbuf ; reading beyond buffer?
	jb	cminb1			; b = no
	mov	ah,prstr
	mov	dx,offset cmer09	; command too long
	int	dos
	jmp	prserr			; overflow = parse error

cminb1:	push	bx
	mov	bx,cmrptr		; read pointer
	mov	ah,[bx]			; get current command char while here
	cmp	bx,cmwptr		; do we need to read more?
	pop	bx			; no if cmrptr < cmwptr
	jb	cminb2			; b: cmrptr < cmwptr (have extra here)
	call	cmgetc			; no readahead, read another into al
	mov	ah,al			; keep char in 'ah'
	cmp	read_source,take_sub	; substitution?
	je	cminb1a			; e = yes
	push	bx
	mov	bx,cmrawptr		; where raw writes go (inc remakes)
	mov	[bx],ah			; store byte
	inc	cmrawptr
	pop	bx
cminb1a:
	push	bx
	mov	bx,cmwptr		; get the pointer into the buffer
	mov	[bx],ah			; put it in the buffer
	inc	bx
	mov	cmwptr,bx		; inc write pointer
	pop	bx
	jmp	short cminb1		; call cmgetc until cmwptr >= cmrptr
					; Char to be delivered is in ah
cminb2:	cmp	in_showprompt,0		; Non-zero when in showprompt
	jne	cminb5			; ne = yes, no editing

	cmp	ah,'W' and 1fh		; is it a ^W?
	jne	cminb3			; ne = no
	call	cntrlw			; kill the previous word
	jmp	repars			; need a new command scan (cleans stk)

cminb3:	cmp	ah,'U' and 1fh		; is it a ^U?
	jne	cminb3a			; ne = no
	mov	cmwptr,offset cmdbuf	; reset buffer write pointer
	mov	cmrptr,offset cmdbuf
	mov	cmrawptr,offset rawbuf	; clear raw buffer
	jmp	repars			; go start over (cleans stack)
					; BS and DEL
cminb3a:cmp	ah,DEL			; delete code?
	je	cminb3b			; e = yes
	cmp	ah,BS			; Backspace (a delete operator)?
	jne	cminb4			; ne = no
cminb3b:call	bufdel			; delete char from buffer
	jc	cminb3c			; c = did erasure
	jmp	cminbf			; no erasure, ignore BS, get more
cminb3c:jmp	repars			; could have deleted previous token

cminb4:	push	bx			; look for hyphen or \hyphen
	cmp	ah,CR			; check for hyphen line continuation
	jne	cminb4b			; ne = not end of line
	mov	bx,cmwptr		; get the pointer into the buffer
	cmp	bx,offset cmdbuf+2	; do we have a previous char?
	jb	cminb4b			; b = no
	cmp	byte ptr[bx-2],'-'	; previous char was a hyphen?
	jne	cminb4b			; ne = no
	pop	bx
	sub	cmwptr,2		; back over -<cr> for both buffers
	sub	cmrawptr,2
	jmp	repars			; reparse what we now have
cminb4b:pop	bx
					; Echoing done here
	cmp	comand.cmquiet,0	; quiet mode?
	jne	cminb5			; yes, skip echoing
	cmp	comand.cmdirect,0	; direct reading?
	jne	cminb4a			; ne = yes, do echoing
	cmp	taklev,0		; in a take file?
	je	cminb4a			; e = no

	cmp	read_source,take_comand	; command reread macro?
	je	cminb4a			; e = yes, echo
	cmp	flags.takflg,0		; echo take file?
	je	cminb5			; e = no
	cmp	read_source,take_sub	; don't echo string substitution
	je	cminb5

cminb4a:push	ax			; save the char
	cmp	ah,' '			; printable?
	jae	cminb4c			; yes, no translation needed
	cmp	ah,CR			; this is printable
	je	cminb4c
	cmp	ah,LF
	je	cminb4c
	cmp	ah,ESCAPE		; escape?
	je	cminb4d			; do not echo this character
	push	ax			; show controls as caret char
	push	dx
	mov	dl,5eh			; caret
	mov	ah,conout
	int	dos
	pop	dx
	pop	ax
	add	ah,'A'-1		; make control code printable
cminb4c:push	dx
	mov	dl,ah
	mov	ah,conout
	int	dos			; echo it ourselves
	pop	dx
cminb4d:pop	ax			; and return char in ah

cminb5:	cmp	ah,CR			; carriage return?
	je	cminb6			; e = yes
	cmp	ah,LF			; line feed?
	je	cminb6
	cmp	ah,FF			; formfeed?
	jne	cminb7			; none of the above, report bare char
	call	fcmblnk			; FF: clear the screen and
	push	bx
	push	cx
	push	dx
	call	flocate			; Home the cursor
	mov	bx,cmwptr		; make the FF parse like a cr
	mov	byte ptr [bx-1],cr	; pretend a carriage return were typed
	pop	dx
	pop	cx
	pop	bx
cminb6: cmp	cmwptr,offset cmdbuf	; parsed any chars yet?
	jne	cminb7			; ne = yes
	cmp	comand.cmcr,0		; bare cr's allowed?
	jne	cminb7			; ne = yes
	jmp	prserr			; If not, just start over
cminb7:	clc
	ret
cminbf	endp

; Read chars from cminbf. Cmrptr points to next char to be read.
; Compresses repeated spaces if cmsflg is non-zero. Exit with cmrptr pointing
; at a terminator or otherwise at next free slot.
; Non-space then space acts as a terminator but cmrptr is incremented.
; Substitution variables, '\%x', are detected and expanded. Return char in AH.

CMGTCH	proc	near			; return char in AH, from rescan buf
	cmp	in_reparse,0		; replaying material?
	je	cmgtc0			; e = no
	call	remake			; remake command line as macro

cmgtc0:	cmp	replay_cnt,0		; replay with no echo?
	jne	cmgtc1			; ne = yes, have in cmdbuf now
	call	cminbf			; get char from buffer or user
cmgtc1:	push	bx
	mov	bx,cmrptr		; get read pointer into the buffer
	mov	ah,[bx]			; read the next char
	inc	bx
	mov	cmrptr,bx		; where to read next time
	cmp	bx,cmwptr		; still reading analyzed text?
	pop	bx
	jb	cmgtc2			; b = yes, don't reexpand
	cmp	replay_cnt,0		; replaying?
	jne	cmgtc2			; ne = yes
	call	subst			; examine for text substitution
	jnc	cmgtc2			; nc = no substitutions done
	jmp	cmgtch			; reread input material

cmgtc2:	push	bx
	mov	bx,replay_cnt		; count down replay bytes
	or	bx,bx	
	jz	cmgtc2a			; z = empty 
	dec	replay_cnt
cmgtc2a:pop	bx
	cmp	ah,' '			; space?
	jne	cmgtc3			; ne = no
	cmp	bracecnt,0		; are we within braces?
	jne	cmgtc3			; ne = yes, treat space as literal
	cmp	cmsflg,0		; space flag, was last char a space?
	jne	cmgtch			; ne = yes, get another char
	mov	cmsflg,0FFH		; set the space(s)-seen flag
	stc				; set carry for terminator
	ret				; return space as a terminator
cmgtc3: mov	cmsflg,0		; clear the space-seen flag
	cmp	ah,braceop		; opening brace?
	jne	cmgtc3b			; ne = no
	inc	bracecnt		; count it
	jmp	short cmgtc3c
cmgtc3b:cmp	ah,bracecl		; closing brace?
	jne	cmgtc3c			; ne = no
	sub	bracecnt,1		; count down and get a sign bit
	jns	cmgtc3c			; ns = no underflow
	mov	bracecnt,0		; catch underflows
cmgtc3c:cmp	ah,escape		; terminators remain in buffer but
	je	cmgtc4			;  are ready to be overwritten
	cmp	ah,'?'			; is the user curious?
	jne	cmgtc3a			; ne = no
	cmp	taklev,0		; in a Take file?
	jne	cmgtc3d			; ne = yes, make query ordinary char
	je	cmgtc4
cmgtc3a:cmp	ah,CR			; these terminators?
	je	cmgtc4			; e = yes
	cmp	ah,LF
	je	cmgtc4
	cmp	ah,FF
	je	cmgtc4
cmgtc3d:clc				; carry clear for non-terminator
	ret
cmgtc4:	dec	cmrptr			; point at terminating char
	stc				; set carry to say it is a terminator
	ret
cmgtch	endp

; Reset cmdbuf write pointer (cmwptr) to where the read pointer
; (cmrptr) is now. Discards material not yet read.
bufreset proc	near
	push	cmrptr			; where next visible char is read
	push	ax			; count removed curly braces
	push	si
	mov	si,cmrptr		; where to look
	mov	cx,cmwptr		; last place being removed
	xor	ah,ah			; get a null
	sub	cx,si			; length to examine
	jle	bufres3a		; le = nothing to scan
	cld
bufres1:lodsb
	cmp	al,braceop		; opening brace, counted already?
	jne	bufres2			; ne = no
	dec	bracecnt		; uncount it
	jmp	short bufres3
bufres2:cmp	al,bracecl		; closing brace, counted already?
	jne	bufres3			; jne = no
	inc	bracecnt		; uncount it
bufres3:loop	bufres1
bufres3a:cmp	bracecnt,ah		; negative?
	jge	bufres4			; ge = no
	mov	bracecnt,ah
bufres4:pop	si
	pop	ax
	pop	cmwptr			; where new char goes in buffer
	ret
bufreset endp

; Delete character from screen and adjust buffer. Returns carry clear if
; no erasure, carry set otherwise.
bufdel	proc	near
	cmp	read_source,take_sub	; substitution macro?
	je	bufdel0			; e = yes, does not change cmrawptr
	cmp	cmrawptr,offset rawbuf	; anything written to base level buf?
	ja	bufdel7			; a = yes
	mov	cmrawptr,offset rawbuf	; safety
	mov	ax,offset cmdbuf
	mov	cmrptr,ax
	mov	cmwptr,ax
	clc				; do nothing
	ret
bufdel7:dec	cmrawptr		; backup one byte
	push	bx
	mov	bx,cmrawptr
	cmp	byte ptr [bx],BS	; last read was BackSpace?
	pop	bx
	jne	bufdel8			; ne = not BS
	dec	cmrawptr		; backup write pointer over BS too
bufdel8:cmp	cmrawptr,offset rawbuf	; sanity check, back too far?
	jae	bufdel9			; ae = not too far
	mov	cmrawptr,offset rawbuf	; reset to start of buffer
bufdel9:cmp	comand.cmdirect,0	; doing direct reading?
	jne	bufdel0			; ne = yes
	mov	ax,offset cmdbuf
	mov	cmwptr,ax		; reset normal parse buffer
	mov	cmrptr,ax
	mov	bracecnt,0
	mov	in_reparse,1		; say need to replay from rawbuf
	jmp	repars			; reparse command

bufdel0:cmp	cmrptr,offset cmdbuf	; at start of buffer now?
	jbe	bufdel5			; be = yes
	push	ax
	push	si
	mov	si,cmrptr
	mov	al,[si]
	dec	si
	xor	ah,ah			; get a null
	cmp	al,braceop		; opening brace, counted already?
	jne	bufdel1			; ne = no
	dec	bracecnt		; uncount it
	jmp	short bufdel2
bufdel1:cmp	al,bracecl		; closing brace?
	jne	bufdel2			; ne = no
	inc	bracecnt		; uncount it
bufdel2:cmp	bracecnt,ah		; negative?
	jge	bufdel3			; ge = no
	mov	bracecnt,ah
bufdel3:pop	si
	pop	ax
	mov	replay_cnt,0		; clear protected replay byte count
	dec	cmrptr			; remove previous char from buffer
	cmp	cmrptr,offset cmdbuf	; back too far?
	jae	bufdel4			; ae = no, material can be erased
	mov	cmrptr,offset cmdbuf	; set to start of buffer
	mov	bracecnt,ah		; ensure this is now cleared
bufdel5:call	bufreset		; reset buffer
	clc				; say no erasure
	ret
bufdel4:call	bufreset		; reset buffer
	stc				; say did erasure
	ret
bufdel	endp

; Remake the current command line, omitting substitution macro contents,
; as a macro (kind is comand, almost "macro"). Current command line is
; in rawbuf, with cmrawptr pointing next place to write.
; Closes currently active substitution macros.
remake	proc	near
	push	bx
	push	cx
	push	dx
	push	di
	mov	cmrptr,offset cmdbuf
	mov	cmwptr,offset cmdbuf	; clear work buffer
	mov	cx,cmrawptr		; where to write next
	mov	cmrawptr,offset rawbuf
	sub	cx,offset rawbuf	; length of string
	jle	remake3			; le = nothing to do
remake1:cmp	taklev,0		; in a take file?
	je	remake2			; e = no
	mov	bx,takadr
	cmp	[bx].taktyp,take_sub	; sub macro?
	jne	remake2			; ne = no
	push	cx
	call	ftakclos		; close sub macros
	pop	cx
	jmp	short remake1		; next victim
remake2:call	takopen_macro		; open take as macro (comand kind)
	mov	bx,takadr		; pointer to new Take structure
	mov	[bx].taktyp,take_comand ; that's us
	and	[bx].takattr,not take_autocr ; no CR at end of macro please
	mov	[bx].takcnt,cx		; number of unread bytes
	mov	[bx].takbuf,seg rawbuf	; segment of Take buffer
	mov	cx,offset rawbuf
	mov	[bx].takptr,cx 		; offset to read from
	mov	cmrawptr,cx
remake3:mov	in_reparse,0		; clear our flag
	mov	bracecnt,0
	mov	replay_cnt,0
	mov	comand.cmkeep,0
	pop	di
	pop	dx
	pop	cx
	pop	bx
	ret
remake endp

; Come here is user types ^W when during input. Remove word from buffer.
cntrlw	proc	near
	push	ax
	push	cx
	push	dx
	call	bufreset		; truncate buffer at cmrptr
	mov	si,cmwptr		; assumed source pointer
	mov	cx,cmrptr		; read pointer
	sub	cx,offset cmdbuf	; compute chars in buffer
	cmp	comand.cmdirect,0	; direct reading?
	jne	cntrl3			; ne = yes, only one buffer

	dec	cmrawptr		; back over ^W
	mov	si,cmrawptr		; use raw buf as source pointer
	mov	cx,si			; point at last written byte
	sub	cx,offset rawbuf	; count of bytes before ^W
	jle	ctlw2			; le = none
cntrl3:	clc				; say have not yet modified line
	jcxz	ctlw2			; z = nothing to do, exit no-carry
	push	es
	std				; scan backward
	mov	ax,ds
	mov	es,ax			; point to the data are
	mov	di,si			; looking from here
	dec	di
	mov	al,' '
	repe	scasb			; look for non-space
	je	ctlw1			; all spaces, nothing to do
	inc	di			; move back to non-space
	inc	cx
	repne	scasb			; look for a space
	jne	ctlw1			; no space, leave ptrs alone
	inc	di
	inc	cx			; skip back over space
ctlw1:	inc	di
	pop	es
	cld				; reset	direction flag
	mov	cmwptr,di		; update pointer
	cmp	comand.cmdirect,0	; direct reading?
	jne	ctlw2			; ne = yes, only one buffer
	inc	di			; leave a char for bufdel
	mov	cmrawptr,di		; setup delete initial byte of word
	call 	bufdel			; delete item, reparse
ctlw2:	pop	dx
	pop	cx
	pop	ax
	ret
cntrlw	endp

; Jump to REPARS to do a rescan of the existing buffer.
; Jump to PRSERR on a parsing error (quits command, clears old read material)

PRSERR	PROC FAR
	mov	cmwptr,offset cmdbuf	; initialize write pointer
	mov	ah,prstr
	mov	dx,offset crlf		; leave old line, start a new one
	int	dos
	call	rprompt			; restore master prompt level
					; reparse current line
REPARS:	mov	cmrptr,offset cmdbuf	; reinit read pointer
	mov	comand.cmper,0		; reset to variable recognition
	mov	cmsflg,0ffh		; strip leading spaces
	mov	subcnt,0		; clear substitution state variables
	mov	bracecnt,0
	cmp	in_showprompt,0		; redoing the prompt itself?
	jne	prser3			; ne = yes, suppress display
	cmp	comand.cmdirect,0	; doing direct reading?
	jne	prser2			; ne = yes
	cmp	taklev,0		; in Take cmd?
	je	prser2			; e = no
	cmp	flags.takflg,0		; echo contents of Take file?
	je	prser3			; e = no
prser2:	call	fctlu			; clear display's line, reuse it
	mov	dx,comand.cmprmp	; display the asciiz prompt
	call	fprtasz
prser3:	mov	bx,0ffffh		; returned keyword value
	mov	sp,comand.cmostp	; set new sp to old one
	jmp	dword ptr comand.cmrprs	; jump to just after the prompt call
PRSERR	ENDP

; Restore prompt material to that of the master prompt. This removes settings
; of local PROMPT calls so we can reprompt at the main Kermit level.
RPROMPT	proc	near
	push	ax			; Must preserve AX
	mov	replay_cnt,0
	mov	ax,mcmprmp		; address of prompt string
	or	ax,ax			; any address given yet?
	jz	rprompt1		; z = none, not inited yet
	mov	comand.cmprmp,ax	; set current address ptr
	mov	ax,word ptr mcmrprs	; offset of reparse address
	mov	word ptr comand.cmrprs,ax
	mov	ax,word ptr mcmrprs+2	; segment of reparse address
	mov	word ptr comand.cmrprs+2,ax
	mov	ax,mcmostp		; stack ptr at reparse time
	mov	comand.cmostp,ax
	mov	cmrawptr,offset rawbuf	; base/raw buffer write ptr, reset
	mov	in_reparse,0		; clear buffer remake flag
rprompt1:pop	ax
	ret
RPROMPT	endp

; write \v(ARGC) contents to ds:di
wrtargc	proc	near
	xor	ax,ax
	cmp	taklev,0		; in a Take/Macro?
	je	wrtarg1			; e = no
	mov	bx,takadr		; current Take structure
	mov	ax,[bx].takargc		; get ARGC
wrtarg1:call	fdec2di			; write as ascii
	mov	word ptr es:[di],0020h	; space and null terminator
	ret
wrtargc	endp

; write \v(COUNT) text to ds:di
wrtcnt	proc	near
	xor	ax,ax
	cmp	taklev,0		; in a Take/Macro?
	je	wrtcnt1			; e = no
	mov	bx,takadr		; current Take structure
	mov	ax,[bx].takctr		; get COUNT
wrtcnt1:call	fdec2di			; write as ascii
	ret
wrtcnt	endp

; write \v(DATE) text to ds:di
wrtdate	proc	near
	push	cx
	push	dx
	mov	ah,getdate		; DOS date (cx= yyyy, dh= mm, dl= dd)
	int	dos
	xor	ah,ah
	cmp	tdfmt,0			; USA standard mm/dd/yyyy?
	jne	wrtdat1			; ne = no
	xor	ah,ah
	mov	al,dh			; month
	call	wrtdat5			; output
	mov	byte ptr es:[di],'/'	; separate
	inc	di
	xor	ah,ah
	mov	al,dl			; day
	call	wrtdat5
	mov	byte ptr es:[di],'/'
	inc	di
	mov	ax,cx
	jmp	short wrtdat3

wrtdat1:cmp	tdfmt,1			; European standard dd/mm/yyyy?
	jne	wrtdat2			; ne = no
	xor	ah,ah
	mov	al,dl			; day
	call	wrtdat5
	mov	byte ptr es:[di],'/'
	inc	di
	xor	ah,ah
	mov	al,dh			; month
	call	wrtdat5
	mov	byte ptr es:[di],'/'
	inc	di
	mov	ax,cx
	jmp	short wrtdat3

wrtdat2:mov	ax,cx			; Japan yyyy:mm:dd,   year
	call	wrtdat5
	mov	byte ptr es:[di],':'
	inc	di
	xor	ah,ah
	mov	al,dh			; month
	call	wrtdat5
	mov	byte ptr es:[di],':'
	inc	di
	xor	ah,ah
	mov	al,dl			; day
wrtdat3:call	wrtdat5
	mov	word ptr es:[di],0020h	; space and null terminator
	pop	dx
	pop	cx
	ret

wrtdat5:cmp	ax,10			; leading tens digit present?
	jae	wrtdat6			; ae = yes
	mov	byte ptr es:[di],'0'	; insert leading 0
	inc	di
wrtdat6:call	fdec2di			; write decimal asciiz to buffer
	ret

wrtdate	endp

; write \v(ERRORLEVEL) text to ds:di
wrterr	proc	near
	mov	al,errlev		; current Errorlevel
	xor	ah,ah
	call	fdec2di			; write as ascii
	mov	word ptr es:[di],0020h	; space and null terminator
	ret
wrterr	endp

; write \v(KEYBOARD) text to ds:di
wrtkbd	proc	near
	mov	ax,keyboard		; 88 or 101 keyboard keys
	call	fdec2di			; write as ascii
	mov	word ptr es:[di],0020h	; space and null terminator
	ret
wrtkbd	endp

; write \v(NDATE) text to ds:di
; where NDATE is YYYYMMDD
wrtndate proc	near
	mov	ah,getdate		; DOS date (cx= yyyy, dh= mm, dl= dd)
	int	dos
	push	dx			; save dx
	mov	ax,cx			; year
	call	fdec2di			; convert it
	pop	dx			; get mm:dd
	push	dx
	mov	al,dh			; months are next
	xor	ah,ah
	cmp	al,10			; less than 10?
	jae	wrtndat1		; ae = no
	mov	byte ptr es:[di],'0'	; leading 0
	inc	di
wrtndat1:call	fdec2di
	pop	dx
	mov	al,dl			; get days
	xor	ah,ah
	cmp	al,10			; less than 10?
	jae	wrtndat2		; ae = no
	mov	byte ptr es:[di],'0'	; leading 0
	inc	di
wrtndat2:call	fdec2di
	mov	word ptr es:[di],0020h	; space and null terminator
	ret
wrtndate endp

; write \v(DIRECTORY) text to es:di
wrtdir	proc	near
	push	si
	mov	ah,gcurdsk		; get current disk
	int	dos
	add	al,'A'			; make al = 0 == 'A'
	stosb
	mov	ax,'\:'
	stosw
	mov	si,di			; work buffer
	push	ds
	mov	cx,es
	mov	ds,cx
	mov	ah,gcd			; get current directory
	xor	dl,dl			; use current drive
	int	dos			; get ds:si = asciiz path (no drive)
	pop	ds
	mov	dx,si
	push	ds
	mov	cx,es
	mov	ds,cx
	call	fstrlen
	pop	ds
	add	di,cx
wrtdir1:mov	word ptr es:[di],0020h	; space and null terminator
	pop	si
	ret
wrtdir	endp

fwrtdir	proc	far
	call	wrtdir
	ret
fwrtdir	endp

; write \v(PLATFORM) text to ds:di
wrtplat	proc	near
	push	si
	mov	si,offset machnam	; machine name in sys dep file
	cld
wrtplat1:lodsb				; get a char
	cmp	al,'$'			; terminator?
	je	wrtplat2		; e = yes
	stosb				; store char
	jmp	short wrtplat1		; keep going
wrtplat2:mov	word ptr es:[di],0020h	; space and null terminator
	pop	si
	ret
wrtplat	endp

; write \v(PORT) text to ds:di
wrtport	proc	near
	push	bx
	push	si
	mov	al,flags.comflg		; get coms port indicator
	mov	bx,offset comptab	; table of comms ports
	mov	cl,[bx]			; number of entries
	xor	ch,ch
	inc	bx
wrtpor3:mov	dx,[bx]			; length of this entry
	mov	si,bx
	add	si,2			; points to entry text string
	add	si,dx			; point to qualifier
	cmp	[si],al			; our port?
	je	wrtpor4			; e = yes
	add	bx,[bx]			; add text length
	add	bx,4			; plus count and qualifier
	loop	wrtpor3			; next entry
	jmp	short wrtpor5		; no match, curious
wrtpor4:mov	si,bx			; point at entry
	add	si,2			; point at string
	mov	cx,[bx]			; length of string
	cld
	rep	movsb			; copy to DS:DI
wrtpor5:mov	word ptr es:[di],0020h	; space and null terminator
	pop	si
	pop	bx
	ret
wrtport	endp

; write \v(PROGRAM) text to ds:si
wrtprog	proc	near
	push	si
	mov	si,offset progm		; source string
	cld
wrtprg1:lodsb
	cmp	al,'$'			; terminator?
	je	wrtprg2			; e = yes
	stosb				; store the char
	jmp	short wrtprg1
wrtprg2:mov	word ptr es:[di],0020h	; space and null terminator
	pop	si
	ret
wrtprog	endp

; write \v(STATUS) text to ds:di
wrtstat	proc	near
	mov	ax,kstatus		; Kermit status word
	call	fdec2di
	mov	word ptr es:[di],0020h	; space and null terminator
	ret
wrtstat	endp

; write \v(SYSTEM) text to ds:di
wrtsystem proc	near
	push	si
	mov	si,offset system	; system string "MS-DOS", dollar sign
	cld
	jmp	wrtplat1		; use some common code
wrtsystem endp

; write \v(TIME) text to ds:di
wrttime	proc	near
	mov	ah,gettim		; DOS tod (ch=hh, cl=mm, dh=ss, dl=.s)
	int	dos
	push	dx			; save dx
	xor	ah,ah
	mov	al,ch			; Hours
	cmp	al,10			; leading digit?
	jae	wrttim1			; ae = yes
	mov	byte ptr es:[di],'0'	; make our own
	inc	di
wrttim1:call	fdec2di			; write decimal asciiz to buffer
	mov	byte ptr es:[di],':'
	inc	di
	xor	ah,ah
	mov	al,cl			; Minutes
	cmp	al,10			; leading digit?
	jae	wrttim2			; ae = yes
	mov	byte ptr es:[di],'0'	; make our own
	inc	di
wrttim2:call	fdec2di			; write decimal asciiz to buffer
	mov	byte ptr es:[di],':'
	inc	di
	pop	dx
	xor	ah,ah
	mov	al,dh			; Seconds
	cmp	al,10			; leading digit?
	jae	wrttim3			; ae = yes
	mov	byte ptr es:[di],'0'	; make our own
	inc	di
wrttim3:call	fdec2di			; write decimal asciiz to buffer
	mov	word ptr es:[di],0020h	; space and null terminator
	ret
wrttime	endp

; write \v(Version) text to ds:di
wrtver	proc	near
	mov	si,offset verident	; MS Kermit version string in mssker
	cld
wrtver1:lodsb
	stosb
	cmp	al,'$'			; end of string?
	jne	wrtver1			; ne = no, continue copying
	dec	di
	mov	word ptr es:[di],0020h	; space and null terminator
	ret
wrtver	endp

; write \v(TERMINAL) text to ds:di
wrtterm	proc	near
	mov	ax,flags.vtflg		; current terminal type
	mov	bx,offset termtb	; terminal type table msx...
	mov	cl,[bx]			; number of entries in our table
	xor	ch,ch
	inc	bx			; point to the data
wrtter1:mov	si,[bx]			; length of keyword
	cmp	ax,[bx+si+2]		; value fields match?
	je	wrtter2			; e = yes
	add	bx,si			; add word length
	add	bx,4			; skip count and value fields
	loop	wrtter1			; keep searching
	jmp	short wrtter4		; no match, use just a space
wrtter2:mov	cx,[bx]			; get length of counted string
	mov	si,bx
	add	si,2			; look at text
	cld
	rep	movsb
wrtter4:mov	word ptr es:[di],0020h	; space and null terminator
	ret
wrtterm	endp

fcmgtch	proc	far
	call	cmgtch
	ret
fcmgtch	endp

code1	ends

code	segment
	assume	cs:code

; Set master prompt level. Enter with DX = offset of prompt string
MPROMPT	proc	near
	mov	mcmprmp,dx		; offset of prompt string
	pop	ax			; get the return address
	mov	word ptr mcmrprs,ax 	; offset to go to on reparse
	mov	mcmostp,sp		; stack pointer at reparse time
	push	ax			; put it on the stack again
	mov	ax,cs			; our current code segment
	mov	word ptr mcmrprs+2,ax 	; segment of reparse address
	mov	comand.cmdirect,0	; read normally (vs force to kbd/file)
	mov	cmrawptr,offset rawbuf	; base/raw buffer write ptr, reset
	mov	cmwptr,offset cmdbuf
	mov	cmrptr,offset cmdbuf
MPROMPT	endp

; This routine prints the prompt and specifies the reparse address.
; Enter with pointer to prompt string in dx. 
PROMPT	PROC  NEAR
	xor	ax,ax			; get a zero
	xchg	al,comand.cmdirect	; set cmdirect to zero for showprompt
	xchg	ah,comand.cmper
	push	ax
	xor	al,al
	xchg	al,flags.takflg		; take echo flag, off in showprompt
	push	ax
	call	showprompt		; convert string with variables
	pop	ax
	xchg	al,flags.takflg		; restore take echo flag
	pop	ax
	mov	comand.cmdirect,al	; restore direct mode (if any)
	mov	comand.cmper,ah
	mov	comand.cmprmp,dx	; save the prompt
	pop	ax			; get the return address
	mov	word ptr comand.cmrprs,ax ; offset to go to on reparse
	mov	comand.cmostp,sp	; save for later restoration
	push	ax			; put it on the stack again
	mov	ax,cs			; our current code segment
	mov	word ptr comand.cmrprs+2,ax ; segment of reparse address
	mov	ax,offset cmdbuf
	mov	cmrptr,ax		; reset buffer read/write pointers
	mov	cmwptr,ax
	mov	cmrawptr,offset rawbuf
	mov	in_reparse,0
	xor	ax,ax
	mov	subcnt,al		; substitution variable state info
	mov	subtype,al
	mov	bracecnt,al
	mov	replay_cnt,ax
	mov	cmsiz,ax
	mov	comand.cmper,al		; allow substitutions
	mov	cmsflg,0ffh		; remove leading spaces
	cmp	comand.cmdirect,al	; doing direct reading?
	jne	promp1			; ne = yes
	cmp	flags.takflg,al		; look at Take flag, zero?
	jne	promp1			; ne=supposed to echo, skip this check
	cmp	taklev,al		; inside a take file?
	je	promp1			; e = no, keep going
	clc
	ret				; yes, return
promp1:	mov	ah,prstr
	mov	dx,offset crlf
	int	dos
	mov	dx,comand.cmprmp	; prompt pointer
	call	prtasz			; show asciiz prompt string
	clc
	ret
PROMPT	ENDP

; Convert ASCIIZ prompt string given in DS:DX to string in DS:comand.cmprmp
; with expansion of substitution variables. Final prompt is in DX and in
; comand.cmprmp = offset cmdbuf+cmdblen/2 (of a 1000 byte total buffer).
showprompt proc far
	cmp	in_showprompt,0		; doing this routine already?
	je	showp0			; e = no
	mov	dx,comand.cmprmp	; yes, return current prompt string
	ret
showp0:	mov	word ptr cmdbuf+cmdblen/2,0	; clear final prompt string
	mov	comand.cmprmp,offset cmdbuf+cmdblen/2 ; prompt pointer
	mov	si,dx			; raw prompt string ds:dx
	call	strlen			; get string length
	or	cx,cx			; empty?
	jnz	showp1			; nz = no
	ret				; return empty prompt string
showp1:	inc	cx			; include null in length
	mov	al,taklev		; entering taklev
	cmp	al,maxtak		; room in take level?
	jb	showp2			; b = yes
	mov	comand.cmprmp,dx	; original raw prompt offset
	ret				; return it

showp2:	push	ax			; save take level
	mov	si,offset cmdbuf	; reset parse buffer pointers
	mov	cmrptr,si
	mov	cmwptr,si
	mov	cmrawptr,offset rawbuf
	xor	ax,ax
	mov	subcnt,al		; clear substitution state variables
	mov	subtype,al
	mov	bracecnt,al
	call	takopen_sub		; open take as text subsitution
	mov	bx,takadr		; address of take structure
	mov	ax,ds
	mov	[bx].takbuf,ax		; segment of Take buffer
	mov	[bx].takptr,dx 		; offset of beginning of def text
	mov	[bx].takcnt,cx		; # of chars in buffer (includes NUL)
	or	[bx].takattr,take_autocr ; imply CR at end of prompt
	mov	in_showprompt,1		; say making new prompt
	xor	ax,ax
	mov	cmsflg,al		; get all spaces
	mov	comand.cmper,al		; reset to variable recognition
	mov	comand.cmquiet,1	; no echo
	mov	comand.cmwhite,1	; leading white space is ok
	mov	comand.cmcr,1		; bare cr's allowed
	mov	comand.cmostp,sp	; set reprompt stack pointer here
	mov	word ptr comand.cmrprs,offset showp3 ; offset for reparse
	mov	ax,cs			; our current code segment
	mov	word ptr comand.cmrprs+2,ax ; segment of reparse address

showp3:	mov	cmptab,offset cmdbuf+cmdblen/2 ; buffer for new string
	mov	comand.cmblen,(cmdblen/2)-100 ; length of our buffer (plenty)

showp4:	call	fcmgtch			; get a char
	jnc	showp5			; nc = non-terminator
	cmp	ah,' '			; space terminator?
	je	showp5			; e = yes
	inc	cmrptr			; 'read' other kinds of terminator
showp5:	mov	bx,cmptab		; pointer into destination array
	mov	[bx],ah			; put char into the buffer
	inc	bx
	xor	al,al
	mov	[bx],al			; insert null terminator
	mov	cmptab,bx
	cmp	bx,offset cmdbuf+cmdblen ; end of buffer?
	jae	showp6			; ae = end of buffer, terminate
	mov	cmsflg,0		; still get spaces
	or	ah,ah			; this terminator?
	jnz	showp4			; nz = no, keep reading

showp6:	pop	ax			; saved entering taklev
	cmp	al,taklev		; saved versus current taklev
	jae	showp7			; ae = nothing new to be removed
	push	ax			; save again for loop
	call	takclos			; close Take file
	jmp	showp6			; keep at it
showp7:	xor	ax,ax
	mov	comand.cmwhite,al	; clear leading whitespace flag
	mov	comand.cmblen,ax	; set user buffer length to unknown
	mov	comand.cmquiet,al	; enable echo
	mov	comand.cmcr,al		; bare cr's not allowed
	mov	in_showprompt,al	; done making new prompt
	mov	dx,offset cmdbuf+cmdblen/2 ; return in DX
	mov	comand.cmprmp,dx	 ; returned prompt pointer
	mov	subcnt,al
	mov	subtype,al
	ret
showprompt endp

ISDEV	PROC	NEAR			; Set carry if STDIN is non-disk
	push	ax
	push	bx
	push	dx
	xor	bx,bx			; handle 0 is stdin
	xor	al,al			; get device info
	mov	ah,ioctl
	int	dos
	rcl	dl,1			; put ISDEV bit into the carry bit
	pop	dx			; carry is set if device
	pop	bx
	pop	ax
	ret				; carry set if device
ISDEV	ENDP

ISEOF	PROC	NEAR			; Set carry if STDIN is at EOF
	push	ax			;  but only if stdin is a non-device
	push	bx
	push	dx
	xor	bx,bx			; handle 0 is stdin
	xor	al,al			; get device info
	mov	ah,ioctl
	int	dos
	mov	ah,ioctl
	mov	al,6			; get handle input status, set al
	test	dl,80h			; bit set if handle is for a device
	jnz	iseof1			; nz = device, always ready (al != 0)
	int	dos
iseof1:	or	al,al			; EOF?
	pop	dx
	pop	bx
	pop	ax
	jnz	iseof2			; nz = no
	stc				; set carry for eof
	ret
iseof2:	clc				; clear carry for not-eof
	ret
ISEOF	ENDP

; Convert ascii characters in al and ah to lowercase.
; All registers are preserved except AX, of course.

TOLOWR PROC NEAR
	cmp	ah,'A'			; less that cap A?
	jl	tolow1			; l = yes. leave untouched
	cmp	ah,'Z'+1		; more than cap Z?
	jns	tolow1			; ns = yes
	or	ah,20H			; convert to lowercase
tolow1:	cmp	al,'A'			; less that cap A?
	jl	tolow2			; l = yes. leave untouched
	cmp	al,'Z'+1		; more than cap Z?
	jns	tolow2			; ns = yes
	or	al,20H			; convert to lowercase
tolow2:	ret
TOLOWR	endp

TOUPR PROC FAR
	cmp	ah,'a'			; less that lower A?
	jb	toup1			; l = yes. leave untouched
	cmp	ah,'z'			; more than lower Z?
	ja	toup1			; ns = yes
	and	ah,not 20H		; convert to upper case
toup1:	cmp	al,'a'			; less that lower A?
	jb	toup2			; l = yes. leave untouched
	cmp	al,'z'			; more than lower Z?
	ja	toup2			; ns = yes
	and	al,not 20H		; convert to lowercase
toup2:	ret
TOUPR	endp
code	ends

code1	segment
	assume	cs:code1

; Parse control sequences and device control strings.
; Expect CSI, Escape [, or DCS lead-in characters to have been read.
; Puts numerical Parameters in array param (16 bits, count is nparam) and
;  a single letter Parameter in lparam, (Parameters are all ASCII column 3)
;  Intermediate characters in array inter (count is ninter), (ASCII column 2)
;  Final character in AL (ASCII columns 4-7).
; Invoke by setting state to offset atparse, set pardone to offset of
; procedure to jump to after reading Final char (0 means do just ret)
; and optionally setting parfail to address to jump to if parsing failure.
; When the Final char has been accepted this routine jumps to label held in
; pardone for final action. Before the Final char has been read successful
; operations return carry clear.
; Failure exits are carry set, and an optional jump through parfail (if 
; non-zero) or a return.

atparse	proc	near
	mov	bx,parstate		; get parsing state
	or	bx,bx			; have any state?
	jnz	atpars1			; nz = have a state
	call	atpclr			; do initialization
	mov	bx,parstate		; get initial state
atpars1:call	bx			; execute it
	jc	atpfail			; c = failure
	cmp	parstate,offset atpdone	; parsed final char?
	je	atpdone			; e = yes
	ret				; no, wait for another char

				; successful conclusion, final char is in AL
atpdone:mov	parstate,0		; reset parsing state
	cmp	pardone,0		; separate return address defined?
	jne	atpdon1			; ne = yes
	clc
	ret				; else just return
atpdon1:clc
	jmp	pardone			; jmp to supplied action routine

atpfail:mov	parstate,0		; failed, reset parser to normal state
	cmp	parfail,0		; jump address specified?
	je	atpfail1		; e = no
	jmp	parfail			; yes, exit this way
atpfail1:stc
	ret
					; parsing workers
atparm:	cmp	ninter,0		; Parameter, started intermediate yet?
	jne	atinter			; ne = yes, no more parameters
	cmp	al,';'			; argument separator?
	jne	atparm3			; ne = no
	mov	ax,nparam		; number of Parameters
	inc	ax			; say a new one
	cmp	ax,maxparam		; too many?
	jb	atparm2			; b = no, continue
	stc				; set carry to say failed
	ret				; too many, ignore remainder
atparm2:mov	nparam,ax		; say doing another Parameter
	clc
	ret

atparm3:mov	ah,al			; copy char
	and	ah,not 0fh		; ignore low nibble
	cmp	ah,30h			; column 3, row 0? (30h='0')
	jne	atparm6			; ne = no, check Intermediate/Final
	cmp	al,'9'			; digit?
	ja	atparm5			; a = no, check letter Parameters
	sub	al,'0'			; ascii to binary
	mov	bx,nparam		; current parameter number
	shl	bx,1			; convert to word index
	mov	cx,param[bx]		; current parameter value
	shl	cx,1			; multiply by 10.  2 * cl
	push	bx
	mov	bx,cx			; save 2 * cl
	shl	cx,1			; 4 * cl
	shl	cx,1			; 8 * cl
	add	cx,bx			; 10 * cl
	pop	bx
	add	cl,al			; add new digit
	adc	ch,0
	jnc	atparm4			; nc = no carry out (65K or below)
	mov	cx,0ffffh		; set to max value
atparm4:mov	param[bx],cx		; current Parameter value
	clc
	ret
					; check non-numeric Parameters
atparm5:cmp	al,'?'			; within column 3?
	ja	atfinal			; a = no, check Final char
	mov	lparam,al		; store non-numeric Parameter
	clc
	ret

atparm6:cmp	nparam,0		; started a parameter yet?
	jne	atparm7			; ne = yes
	cmp	param,0			; got anything for param[0]?
	je	atinter			; e = no
atparm7:inc	nparam			; yes, say finished with another

atinter:mov	parstate,offset atinter	; next state (intermediate)
	cmp	al,';'			; argument separator?
	jne	atinte1			; ne = no
	cmp	ninter,maxinter		; too many intermediates?
	jb	atinte2			; b = no, continue
	stc				; carry = failed
	ret				; too many, ignore remainder
atinte1:test	al,not 2fh		; column two = 20h - 2fh?
	jnz	atfinal			; nz = not an Intermediate, try Final
	cmp	ninter,maxinter		; too many intermediates?
	jb	atinte1a		; b = no, continue
	stc				; carry = failed
	ret				; too many, ignore remainder
atinte1a:mov	bx,ninter		; current Intermediate slot number
	mov	inter[bx],al		; current Intermediate value
atinte2:inc	ninter			; say doing another Intermediate
	clc
	ret

atfinal:cmp	al,40h			; Final character, range is 40h to 7fh
	jb	atfina1			; b = out of range
	cmp	al,7fh
	ja	atfina1			; a = out of range
	mov	parstate,offset atpdone	; next state is "done"
	clc				; success, final char is in AL
	ret
atfina1:stc				; c = failed
	ret
atparse	endp

; Clear Parameter, Intermediate arrays in preparation for parsing
atpclr	proc	near
	push	ax
	push	cx
	push	di
	push	es
	xor	ax,ax			; get a null
	mov	parstate,offset atparm	; init parser state
	mov	lparam,al		; clear letter Parameter
	mov	nparam,ax		; clear Parameter count
	mov	cx,maxparam		; number of Parameter slots
	mov	di,offset param		; Parameter slots
	push	ds
	pop	es			; use data segment for es:di below
	cld				; set direction forward
	rep	stosw			; clear the slots
	mov	ninter,ax		; clear Intermediate count
	mov	cx,maxinter		; number of Intermediate slots
	mov	di,offset inter		; Intermediate slots
	rep	stosb			; clear the slots
	pop	es
	pop	di
	pop	cx
	pop	ax
	ret
atpclr	endp

; Dispatch table processor. Enter with BX pointing at table of {char count,
; address of action routines, characters}. Jump to matching routine or return.
; Enter with AL holding received Final char.
atdispat proc near
	mov	cl,[bx]			; get table length from first byte
	xor	ch,ch
	mov	di,bx			; main table
	add	di,3			; point di at first char in table
	push	es
	push	ds
	pop	es			; use data segment for es:di below
	cld				; set direction forward
	repne	scasb			; find matching character
	pop	es
	je	atdisp2			; e = found a match, get action addr
	ret				; ignore escape sequence
atdisp2:sub	di,bx			; distance scanned in table
	sub	di,4			; skip count byte, address word, inc
	shl	di,1			; convert to word index
	inc	bx			; point to address of action routines
	mov	bx,[bx]			; get address of action table
	jmp	word ptr [bx+di]	; dispatch to the routine
atdispat endp
code1	ends
	end
