		page	66,132
;============================================================================
; ALIAS.COM adds a command line	stack, a command line editor, and
; an alias function to COMMAND.COM.  Syntax is:
;
;	ALIAS [alias [command]]	[/F filename] [/S nn] [/U]
;	      [/B] [/E]	[/D] [/L] [/U] [/*] [//]
;
; where	/B = Set size of buffer	for future alias commands
;	/D = Disable alias translation
;	/E = Enable alias translation
;	/F = Filename of file with list	of commands
;	/L = List aliases currently in list
;	/M = Set minimum command length	to save
;	/S = Size of command line stack	where "nn" is the number of commands
;	     to	save
;	/U = Uninstall the program
;	/* or // = Comment. Ignore remainder of	the line.
;
;
; Revision History:
;
;	Version	1.0	Initial	Release		PC Magazine Vol	8 Num 22
;
;	Version	1.1	Fixed %% bug		January	3, 1989
;			Fixed DOS 3.2 bug
;			Now works in OS/2 DOS box
;			Support	for COMMAND.COM	shelling
;			Reduced	installation memory requirments
;
;============================================================================

		code	segment
		assume	cs:code

		org	2ch
env_segment	dw	?			;Word containing the segment
						;  of the program's env. block.
		org	80h
command_tail	db	?			;Offset	of the command tail.

		org	100h

main:		jmp	initialize
program		db	13,10,"ALIAS 1.1 "
copyright	db	"(c) 1990 Ziff Communications Co.",10,13
authors		db	"PC Magazine ",254," Doug Boling and Jeff Prosise"
		db	10,13,"$",1Ah
;
;Change	the following two numbers to adjust the	size of	alias on installation.
;
filebuf_size	dw	0400h			;Size of input file buffer.
max_code_size	dw	0AF0h			;Max size of installed code.

aliaslist_ptr	dd	?			;Pointer to alias list.
aliaslist_size	dw	?			;Pointer to end	of list	seg
work_buff_ptr	dw	?			;Pointer to alias working buff

chk_alias	db	1			;Alias enable flag.
minlength	db	1			;Minimum len of	cmd to stack
cmdstack_base	dw	?			;Offset	of command stack
cmdstack_size	dw	16			;Size of command stack.

cmdcom_psp	dw	0			;Segment of COMMAND.COM	PSP
master_env	dw	0			;Segment of master environment

cef_pointer	dd	?			;Pointer to critical err flag
dos_version	dw	0			;DOS Version number

mystack_ptr	dw	offset end_of_resident + 512 ;Ptr to internal stack
saved_ss	dw	?
saved_sp	dw	?
int21h		dd	-1			;Int 21	vector (DOS)
int2fh		dd	-1			;Int 2f	vector (DOS MULTIPLEX)

multiplex_id	db	0dbh			;Program ID for	multiplex int

points		dw	?			;Scan lines per	character
columns		db	?			;Number	of screen columns
cursor_mode	dw	?			;Cursor	mode
bufferptr	db	?			;Input buffer pointer
next_ptr	dw	0			;Address where next command
						;  will	be stored
cmd_ptr		dw	0			;Address of current command
						;  in command stack
insert_flag	db	0			;0 = insert off, 1 = on
exkeys		db	71,72,75,77,79,80	;Extended keycode list
		db	82,83,115,116,117
exkeys_end	=	$

ex_entry	dw	offset ctrl_end		;Jump table for	extended
		dw	offset ctrl_right	;  keycodes
		dw	offset ctrl_left
		dw	offset delete
		dw	offset toggle_ins
		dw	offset next_cmd
		dw	offset eol
		dw	offset move_right
		dw	offset move_left
		dw	offset prev_cmd
		dw	offset home

;============================================================================
; DOSINT processes calls to interrupt 21h
;============================================================================
dosint		proc	far
		assume	cs:code,ds:nothing,es:nothing
		cmp	ah,0ah			;Check for char	input
		je	dosint_1		;If so,	continue
goto_dos:
		jmp	cs:[int21h]		;else pass the call to DOS
;
;Compare the active PSP	with COMMAND.COM PSP.
;
dosint_1:
		push	ax			;Save registers
		push	bx
		mov	ah,51h			;Get active PSP	segment
		cmp	word ptr cs:[dos_version],310h
		jb	early_dos
		pushf
		call	cs:[int21h]
		jmp	short chkpsp1
early_dos:
		push	es			;If before DOS 3.1, set
		les	bx,cs:[cef_pointer]	;  critical error flag to
		inc	byte ptr es:[bx]	;  force DOS to	use the	aux
		pushf				;  stack.
		call	cs:[int21h]		;Get active PSP	segment
		mov	ax,bx
		les	bx,cs:[cef_pointer]	;Reset critical	error flag
		dec	byte ptr es:[bx]
		mov	bx,ax
		pop	es
chkpsp1:
		cmp	bx,cs:[cmdcom_psp]	;See if	COMMAND.COM active.
		je	dosint_2

		push	es			;If the	active PSP has an
		mov	es,bx			;  environment block at	a
		mov	ax,es:[2ch]		;  higher memory address than
		dec	ax			;  the PSP, assume that	the
		mov	es,ax			;  the active program is a
		cmp	byte ptr es:[0],"M"	;  shelled copy	of COMMAND.COM
		pop	es
		jne	chkpsp2			;Valid memory block?
		cmp	ax,bx			;Env seg > PSP seg?
		jae	dosint_2		;Yes, must be shelled copy
chkpsp2:
		pop	bx			;Cleanup and goto DOS
		pop	ax
		jmp	short goto_dos
dosint_2:
		cld				;Set direction flag
		mov	cs:[saved_ss],ss	;Save SS:SP
		mov	cs:[saved_sp],sp
		cli
		push	cs			;Move to internal stack
		pop	ss
		mov	sp,cs:[mystack_ptr]
		sti
		push	bp			;Save remaining	registers.
		mov	bp,sp			;Set up	stack access
		push	cx
		push	dx
		push	di
		push	si
		push	ds
		push	es
;
;The call is from COMMAND.COM. Invoke line editor.
;
		push	dx
		call	cmd_input
		pop	dx
		cmp	cs:[cmdstack_size],0
		je	dosint_3
		call	cmd_record
;
;Check for alias before	returning to COMMAND.COM
;
dosint_3:
		cmp	cs:[chk_alias],0	;See if	alias translation
		je	dosint_exit		;  is enabled.
		mov	si,[bp-4]		;Get pointer to	input buffer
		inc	si
		xor	cx,cx
		or	cl,ds:[si]		;Get length of buffer
		je	dosint_exit		;If buffer empty, exit.
		inc	si			;Point to 1st char in buffer
		mov	ax,cs
		mov	es,ax			;Set ES	to installed code.
		call	searchalias		;See if	an alias is found.
		jc	dosint_exit		;No, exit.
;
;If alias found, copy it from alias list to internal buffer.
;
		mov	si,di			;Load SI with alias pointer
		mov	ax,es
		mov	ds,ax			;Point DS to alias list	seg
		xor	cx,cx
		mov	cl,ds:[si+3]		;Get size of alias
		call	getalias		;Get alias from	list
;
;Append	remainder of command line to alias if no cmd line parameters were used.
;
		or	dx,dx			;See if	any command line
		jne	dosint_6		;  parameters were used.
		push	si			;Save pointer to buffer.
		push	ds
		mov	di,si
		add	di,cx			;Point DI to end of alias
		mov	dx,cx			;Save length of	alias
		mov	si,[bp-4]		;Point DS:SI to	command.com
		mov	ds,[bp-10]		;  data	buffer.
		xor	cx,cx
		inc	si
		mov	cl,[si]			;Get length of command line.
		cmp	ah,cl			;See if	enough space in
		ja	dosint_4		;  internal buffer. If not
		mov	cl,ah			;  copy	only until buff	full.
dosint_4:
		inc	si
		mov	bl,1			;Skip past alias.
		call	scan4char
		jc	dosint_5
		dec	si			;Back up 1 char
		inc	cx
		add	dx,cx			;Add length of command line.
		rep	movsb			;Copy command line
dosint_5:
		mov	cx,dx			;Restore length	of command
		pop	ds			;Restore pointer
		pop	si
;
;Copy alias from internal buffer to COMMAND.COM	data buffer.
;
dosint_6:
		mov	di,[bp-4]		;Point ES:DI to	command.com
		mov	es,[bp-10]		;  data	buffer.
		mov	al,es:[di]		;Get size of data buffer
		dec	al			;If alias longer than buffer,
		cmp	al,cl			;  copy	only the enough
		ja	dosint_7		;  characters to fill the
		xor	cx,cx			;  buffer.
		mov	cl,al
dosint_7:
		inc	di			;Move DI past length bytes
		mov	es:[di],cl		;Save length of	command
		inc	di
		rep	movsb			;Copy alias.
		mov	byte ptr es:[di],13	;Append	carriage return
dosint_exit:
		mov	cx,cs:[cursor_mode]	;Set default cursor
		mov	ah,1
		int	10h
		pop	es
		pop	ds
		pop	si
		pop	di
		pop	dx
		pop	cx
		pop	bp
		cli
		mov	ss,cs:[saved_ss]	;Restore stack pointer
		mov	sp,cs:[saved_sp]
		pop	bx
		pop	ax
		iret				;Return	to COMMAND.COM
dosint		endp

;-----------------------------------------------------------------------------
; GETALIAS Copies an alias from	the alias list while substituting any
;	   environment variables and command line parameters.
; Entry:  DS:SI	- pointer to alias
; Exit:	  DS:SI	- pointer to buffer containing translated alias.
;	     AH	- free space in	buffer
;	     CX	- length of the	alias
;-----------------------------------------------------------------------------
getalias	proc	near
		mov	di,work_buff_ptr	;Point DI to internal buffer
		xor	ax,ax			;Point to command by adding
		mov	al,ds:[si+2]		;  the size of the alias to
		add	si,ax			;  pointer to the entry.
		add	si,4			;Move past list	data.
		mov	ah,126			;AH contains max size of buff
		xor	dx,dx			;Clear flag for	line params
alias_1:
		lodsb				;Get byte from alias
		cmp	al,"%"			;See if	special	character
		je	alias_3			;Yes, process special char.
alias_2:
		stosb				;Store byte from alias
		dec	ah			;Dec buffer size counter
		jz	alias_6			;If internal buffer full, done
		loop	alias_1
		jmp	short alias_6		;If at end of alias, done
;
;A percent sign	has been found indicating a 'soft' parameter.
;
alias_3:
;		mov	al,ds:[si]		;Get character after %
		lodsb
		dec	cx
		cmp	al,"%"			;If double %, include in one
		je	alias_2			;  % in	alias.
		mov	bh,al			;Copy and check	to see if
		sub	bh,"0"			;  the next char is a number.
		jb	alias_5			;  If so, assume a line
		cmp	bh,9			;  parameter.
		ja	alias_5
		call	sublineparam		;Substitute a line paramter
		inc	dx
alias_4:
		loop	alias_1
		jmp	short alias_6		;If at end of alias, done
alias_5:
		dec	si			;Backup	to 1st character
		inc	cx
		call	subenvvar		;Substitute an environment var
		loop	alias_1
alias_6:
		mov	si,work_buff_ptr	;Point SI to internal buffer
		mov	cx,di			;Compute size of completed
		sub	cx,si			;  alias.
		ret
getalias	endp

;-----------------------------------------------------------------------------
; SUBLINEPARAM substitutes a parameter from the	command	line into the alias.
; Entry:  DS:SI	- pointer to alias
;	  ES:DI	- pointer to buffer to copy the	line parameter
;	     AH	- remaining space in the internal buffer
;	     BH	- binary number	of the line parameter
;	     CX	- length of the	alias
; Exit:	  ES:DI	- pointer to byte after	the parameter in the buffer
;	  DS:SI	- pointer to the character after the line parameter number
;	     CX	- remaining length of the alias
;-----------------------------------------------------------------------------
sublineparam	proc	near
		push	cx
		push	si
		push	ds
		mov	si,[bp-4]		;Get pointer to	command.com
		mov	ds,[bp-10]		;  data	buffer.
		xor	cx,cx
		mov	cl,ds:[si+1]		;Get number of chars in	buffer.
		inc	si			;Point to the first byte of
		inc	si			;  data.
sublineparam_1:
		or	bh,bh			;Check count of	param to find.
		jz	sublineparam_2
		mov	bl,1
		call	scan4char		;Find next space
		jc	sublineparam_exit
		dec	bl
		call	scan4char		;Find next word
		jc	sublineparam_exit
		dec	bh			;Dec parameter count
		jne	sublineparam_1		;If not	done, loop back.
		dec	si			;Backup	to 1st char in word.
sublineparam_2:
		lodsb				;Get character from parameter
		cmp	al," "			;If space, parameter done
		jbe	sublineparam_exit
		stosb
		dec	ah			;Dec buffer size counter
		jnz	sublineparam_2
sublineparam_exit:
		pop	ds
		pop	si
		pop	cx
		ret
sublineparam	endp

;-----------------------------------------------------------------------------
; SUBENVVAR substitutes	an environment variable	into alias.
; Entry:  DS:SI	- pointer to variable to substitute
;	  ES:DI	- pointer to buffer to copy the	contents of the	variable
;	     AH	- remaining space in internal buffer
;	     CX	- length of alias string
; Exit:	  DS:SI	- pointer to the byte after the	variable name
;	  ES:DI	- pointer to the byte after the	variable contents
;	     CF	- set if variable not found
;-----------------------------------------------------------------------------
subenvvar	proc	near
		push	dx
		push	ds
		push	es
;
;Compute the length of the variable name.
;
		mov	bx,di			;Save pointer to internal buff
		mov	di,si			;Compute the length of the
		mov	dx,cx			;  environment variable	by
		mov	al,"%"			;  searching for the trailing
		repne	scasb			;  % sign.
		sub	dx,cx			;Compute length	of variable.
		dec	dx			;Subtract % byte fron length.
		push	di			;Save ptr to end of env	var.
;
;Search	the Master Environment block for the variable pointed to by DS:SI
;
		mov	es,cs:[master_env]	;Get segment of	master env blk
		xor	di,di			;Point ES:DI to	environment.
		push	cx			;Save alias size.
		push	bx			;Save ptr to internal buffer
		mov	bx,si			;Save pointer to var name
subenvvar_1:
		mov	si,bx			;Get back ptr to var name.
		mov	cx,dx			;Compare env var to var	in
		repe	cmpsb			;  alias.
		je	subenvvar_2		;Variable found, exit loop
		xor	al,al			;Find next environment var.
		mov	cx,-1			;Scan the entire segment.
		repne	scasb
		cmp	byte ptr es:[di],0	;If double zero, end of	env
		jne	subenvvar_1		;  block. else,	loop back.
		pop	di			;Restore DI and	exit
		jmp	short subenvvar_exit
;
;Environment variable found. Substitute	into alias.
;
subenvvar_2:
		mov	si,es			;DS:SI points to env string
		mov	ds,si			;ES:DI points to internal buff
		mov	si,cs
		mov	es,si
		mov	si,di			;Copy pointer to var contents.
		pop	di			;Restore ptr to	internal buff
subenvvar_3:
		lodsb				;Move environment pointer past
		cmp	al,"="			;  the equals sign.
		jne	subenvvar_3
subenvvar_4:
		lodsb				;Move pointer to first
		cmp	al," "			;  non-space character.
		jb	subenvvar_4
subenvvar_5:
		or	al,al			;See if	at the end of variable
		je	subenvvar_exit
		stosb				;Save character	in command
		lodsb				;Get next character
		dec	ah			;Dec buffer size count.
		jne	subenvvar_5		;If buffer not full, continue
subenvvar_exit:
		pop	cx			;Restore alias length
		pop	si			;Restore alias pointer
		pop	es			;Restore segment registers
		pop	ds
		pop	dx
		ret
subenvvar	endp

;-----------------------------------------------------------------------------
; SUBKEY searches the alias list for a key substitution
; Entry:  AL - extended	key code
; Exit:	  CF - clear if	key found, set if not found
;	  DX - offset address of matching entry	in alias list (if CF = 0)
;	  CX - length of matching entry	(if CF = 0)
;-----------------------------------------------------------------------------
subkey		proc	near
		push	bx
		push	di
		push	si
		push	ds
		push	es
		xor	ah,ah			;Indicate extended code.
		les	di,cs:[aliaslist_ptr]	;Get pointer to	alias list.
subkey_1:
		mov	dx,es:[di]		;Get next entry	offset
		cmp	dx,-1			;See if	at the end of the list
		je	subkey_notfound
		cmp	es:[di+4],ax		;Check for key code.
		je	subkey_found		;If found, exit	loop
subkey_2:
		add	di,dx			;Else, point to	next entry.
		jmp	short subkey_1
;
;Copy alias into internal buffer.
;
subkey_found:
		mov	si,di			;Load SI with alias pointer
		mov	ax,es
		mov	ds,ax			;Point DS to alias list	seg
		xor	cx,cx
		mov	cl,ds:[si+3]		;Get size of alias
		call	getalias		;Get alias from	list
		mov	dx,si			;Copy pointer to buffer
		clc				;Set key found flag
subkey_exit:
		pop	es
		pop	ds
		pop	si
		pop	di
		pop	bx
		ret
subkey_notfound:
		stc				;Set key not found flag
		jmp	subkey_exit
subkey		endp

;-----------------------------------------------------------------------------
; SEARCHALIAS searches the alias list for a matching alias.
; Entry:  DS:SI	- pointer to alias
;	     ES	- segment of installed code
;	     CX	- length input buffer
; Exit:	     CF	- clear	if alias found
;	  ES:DI	- pointer to matching entry in alias list, if CF is clear
;-----------------------------------------------------------------------------
searchalias	proc	near
		push	bx			;Save registers
		push	cx
		push	si
		xor	bx,bx
searchalias_1:
		lodsb				;Compute the length of the
		or	al,al			;  length of the alias by
		je	searchalias_2		;  finding the next space.
		cmp	al," "			;Allow zero byte in alias
		jbe	searchalias_3		;  for function	key labels.
searchalias_2:
		inc	bx
		loop	searchalias_1
searchalias_3:
		pop	si
		les	di,es:[aliaslist_ptr]	;Get pointer to	alias list.
		mov	cx,bx			;Get length of alias
searchalias_4:
		mov	bx,es:[di]
		cmp	bx,-1			;See if	at the end of the list
		je	searchalias_notfound
		cmp	es:[di+2],cl		;Compare lengths
		jne	searchalias_5
		push	cx			;Save size and starting
		push	di			;  pointers.
		push	si
		add	di,4			;Point to start	of alias field.
		repe	cmpsb			;Compare alias to input	string
		pop	si
		pop	di
		pop	cx
		je	searchalias_6		;If found, exit	loop
searchalias_5:
		add	di,bx			;Else, point to	next entry.
		jmp	short searchalias_4
searchalias_6:
		clc				;Set alias found flag
searchalias_exit:
		pop	cx
		pop	bx
		ret
searchalias_notfound:
		stc
		jmp	short searchalias_exit
searchalias	endp

;-----------------------------------------------------------------------------
; SCAN4CHAR scans a string to find the first character.
; Entry:  SI - pointer to ASCII	string
;	  BL - 0 = find	next char, 1 = find next space
;	  CX - file length
; Exit:	  AL - first nonspace character
;	  CF - set if carriage return found
;-----------------------------------------------------------------------------
scan4char	proc near
		assume	ds:nothing,es:nothing
scan4loop:
		jcxz	scan4_eol		;See if	at the end of the file.
		lodsb
		dec	cx			;Decrement file	length counter.
		cmp	al,13			;Check for carriage return.
		jne	scan4_1
scan4_eol:
		stc
		jmp	short scan4_exit1
scan4_1:
		or	bl,bl			;Check if searching for	space
		jne	scan4_2			;  or character.
		cmp	al," "			;Check for space or other
		jbe	scan4loop		;  'white' characters.
		jmp	short scan4_exit
scan4_2:
		cmp	al," "			;Check for characters.
		ja	scan4loop
scan4_exit:
		clc
scan4_exit1:
		ret
scan4char	endp

;----------------------------------------------------------------------------
; CMD_INPUT replaces DOS' 0Ah text input function.
;----------------------------------------------------------------------------
cmd_input	proc	near
		push	ds			;Save DS
		xor	ax,ax			;Then zero it
		mov	ds,ax
		mov	ax,ds:[0485h]		;Get number of scan lines
		cmp	ax,cs:[points]		;  per character and branch
		je	cmd1			;  if it hasn't changed

		mov	cs:[points],ax		;Record	new number of scan
		mov	ax,ds:[0460h]		;  lines per character and
		mov	cs:[cursor_mode],ax	;  cursor mode
cmd1:
		pop	ds			;Restore DS
		call	set_cursor		;Set cursor mode

		mov	ah,15			;Get video page	and columns
		int	10h
		dec	ah			;Calculate max column number
		mov	cs:[columns],ah		;Save it
		mov	ax,ds			;Point ES:DI to	buffer
		mov	es,ax
		mov	di,dx
		add	di,2
		mov	si,dx			;Point DS:SI to	character count
		inc	si
		mov	byte ptr [si],0		;Zero initial count
		mov	cs:[bufferptr],1	;Set initial index value
		cld				;Clear DF
;
;Wait for a keycode to appear in the keyboard buffer.
;
getkey:		mov	ah,0Bh			;Check buffer for keycode
		int	21h
		or	al,al			;Anything there?
		jne	getchar			;Yes, then go get it
		int	28h			;No, then execute interrupt 28h
		jmp	getkey			;Loop back for another try
;
;Read the keycode and process it if it's not an extended code.
;
getchar:
		mov	ah,8			;Read character	from buffer
		int	21h
		or	al,al			;Is it an extended code?
		je	excode			;Yes, then branch

		cmp	al,8			;Backspace key?
		jne	getc1			;No, then branch
		call	backspace		;Rub out a character
		jmp	getkey			;Return	to loop
getc1:
		cmp	al,9			;Tab key?
		jne	getc2			;No, then branch
		call	tab			;Tab to	next tab boundary
		jmp	getkey			;Return	to loop
getc2:
		cmp	al,27			;ESC key?
		jne	getc3			;No, then branch
		call	clear_line		;Yes, then clear input line
		mov	cs:[cmd_ptr],0		;Zero current command pointer
		jmp	getkey
getc3:
		cmp	al,7Fh			;Ctrl-Backspace?
		jne	getc4			;No, then branch
		call	ctrl_bs			;Yes, then delete word
		jmp	getkey
getc4:
		cmp	al,13			;ENTER key?
		je	enter_			 ;Yes, then branch
		call	printchar		;No, then print	the character
getc5:
		jmp	getkey
enter_:
		call	eol			;Place cursor at end-of-line
		mov	byte ptr es:[di],13	;Insert	carriage return	code
		mov	ah,2			;Advance to next line
		mov	dl,13
		int	21h
get_exit:
		ret
;
;Process extended keycodes.
;
excode:
		mov	ah,8			;Read extended code
		int	21h

		cmp	al,3Bh			;See if	below F1
		jb	excode3
		cmp	al,71h			;See if	above Alt-F10
		ja	excode3
		cmp	al,54h			;See if	above Shift-F1
		jae	excode1
		cmp	al,44h			;See if	above F10
		ja	excode3
excode1:
		call	subkey			;Scan alias list for match
		jc	getkey			;Exit if no match found
		push	cx
		push	dx
		call	clear_line		;Clear command line
		pop	dx
		pop	cx
excode2:
		push	si
		mov	si,dx			;Copy offset address into SI
		mov	al,byte	ptr cs:[si]	;Get next character
		pop	si
		cmp	al,13			;Exit and execute command
		je	enter_			 ;  on carriage	return
		inc	dx
		push	cx
		push	dx
		call	printchar		;Print it
		pop	dx
		pop	cx
		jc	getc5
		loop	excode2			;Loop until done
		jmp	getkey			;Return	to input loop
excode3:
		push	es			;Save buffer address
		push	di
		mov	cx,cs			;Point ES:DI to	list of
		mov	es,cx			;  supported keycodes
		mov	di,offset exkeys
		mov	cx,offset exkeys_end - offset exkeys
		repne	scasb			;Scan list
		pop	di			;Clear the stack
		pop	es
		jne	excode4			;Ignore	if key not found
		shl	cx,1			;Convert CX to address
		add	cx,offset ex_entry
		push	bx			;Save page number in BH
		mov	bx,cx			;Get entry address from	table
		mov	ax,cs:[bx]
		pop	bx			;Restore BH
		call	ax			;Call handling routine
excode4:
		jmp	getkey			;Return	to input loop
cmd_input	endp

;------------------------------------------------------------------------------
; PREV_CMD outputs the previous	command	in the command stack.
;------------------------------------------------------------------------------
prev_cmd	proc	near
		mov	dx,cs:[cmd_ptr]		;Get current stack index
		cmp	dx,cs:[cmdstack_size]	;Exit if at top	of command
		je	prev_exit		;  stack
		or	dx,dx			;Branch	if not at bottom of
		jnz	prev1			;  command stack
;
;Copy the current contents of the command line to the search buffer.
;
		mov	cl,[si]			;Get count of characters on
		xor	ch,ch			;  command line	in CX
		inc	cx
		push	es			;Save registers
		push	di
		push	si
		mov	ax,cs			;Point ES:DI to	search text
		mov	es,ax			;  buffer
		mov	di,offset command_tail
		rep	movsb			;Copy command line text
		pop	si			;Restore registers
		pop	di
		pop	es
;
;Search	for the	previous command and output it.
;
prev1:
		inc	dx			;Increment stack index
		mov	ax,cs:[next_ptr]	;Get stack base	index
		sub	ax,dx			;Calculate address of previous
		cmp	ax,0			;  command
		jge	prev2
		add	ax,cs:[cmdstack_size]
prev2:
		mov	cl,7			;Calculate offset address of
		shl	ax,cl			;  the command
		add	ax,cs:[cmdstack_base]
		cmp	byte ptr cs:[command_tail],0	;Output	command	if there is
		je	prev3			;  no search criterion
		call	check_string		;Compare strings
		jz	prev3			;Output	command	if the strings
		cmp	dx,cs:[cmdstack_size]	;  match
		jne	prev1
		ret				;Exit if at top	of stack
prev3:
		push	si			;Save SI
		mov	si,ax			;Transfer address to SI
		cmp	byte ptr cs:[si],0	;Valid command?
		pop	si			;Restore SI
		je	prev_exit		;No, then ignore keypress
		mov	cs:[cmd_ptr],dx		;Save new command pointer
		call	write_command		;Output	the command string
prev_exit:
		ret
prev_cmd	endp

;------------------------------------------------------------------------------
; NEXT_CMD outputs the next command in the command stack.
;------------------------------------------------------------------------------
next_cmd	proc	near
		mov	dx,cs:[cmd_ptr]		;Get current stack index
		or	dx,dx			;Exit if it's zero
		jz	next_exit
next1:
		dec	dx			;Decrement stack index
		mov	cs:[cmd_ptr],dx		;Save command pointer
		or	dx,dx			;Clear line and	exit if
		jz	next3			;  the result is zero
		mov	ax,cs:[next_ptr]	;Get stack base	index
		sub	ax,dx			;Calculate address of next
		cmp	ax,0			;  command
		jge	next2
		add	ax,cs:[cmdstack_size]
next2:
		mov	cl,7			;Calculate offset address of
		shl	ax,cl			;  command
		add	ax,cs:[cmdstack_base]
		cmp	byte ptr cs:[command_tail],0	;Output	command	if there is
		je	next4			;  no search criterion
		call	check_string		;Compare strings
		jz	next4			;Output	command	if the strings
		jmp	next1			;  match
next3:
		call	clear_line
		cmp	byte ptr cs:[command_tail],0
		je	next_exit
		mov	ax,offset command_tail
next4:
		call	write_command		;Output	the command string
next_exit:
		ret
next_cmd	endp

;------------------------------------------------------------------------------
; CHECK_STRING compares	the string in the search buffer	with another string.
; Entry:  AX - string address
; Exit:	  ZF - set if strings are equivalent, clear if they are	not
;------------------------------------------------------------------------------
check_string	proc	near
		push	ds			;Save registers
		push	si
		push	es
		push	di
		mov	cx,cs			;Point DS and ES to
		mov	ds,cx			;  code	segment
		mov	es,cx
		mov	si,offset command_tail	;Point DS:SI to	text in
		mov	cl,[si]			;  search buffer
		xor	ch,ch
		inc	si
		mov	di,ax			;Point DI to other string
		inc	di			;
		repe	cmpsb			;Compare strings
		pop	di			;Restore registers
		pop	es
		pop	si
		pop	ds
		ret
check_string	endp

;------------------------------------------------------------------------------
; PRINT_STRING writes an ASCII string to the command line.
; Entry:  DS:SI	- string address
;	     CX	- number of characters
;------------------------------------------------------------------------------
print_string	proc	near
		jcxz	ps_exit			;Exit if no characters
		push	dx
		cld
		mov	ah,2			;Print the character
ps1:
		lodsb				;Get a byte
		mov	dl,al			;Transfer it to	DL
		int	21h			;Output	character
		loop	ps1			;Loop until done
		pop	dx
ps_exit:
		ret
print_string	endp

;------------------------------------------------------------------------------
; WRITE_COMMAND	outputs	a command string.
; Entry:  AX - string offset address
; Exit:	  AL - character after string
;------------------------------------------------------------------------------
write_command	proc	near
		push	ax			;Save address
		call	clear_line		;Clear input line
		pop	ax			;Retrieve string address
		push	ds			;Save DS and SI
		push	si
		push	cs			;Point DS to string segment
		pop	ds
		mov	si,ax			;Point SI to the string
		mov	cl,[si]			;Get string length
		xor	ch,ch
		mov	byte ptr es:[di-1],cl	;Store string length
		inc	si			;Advance SI to string text
write1:
		mov	ah,2			;Print one character
		mov	dl,[si]
		int	21h
		movsb				;Transfer character to buffer
		inc	cs:[bufferptr]		;Advance pointer
		loop	write1			;Loop until done
		lodsb				;Get first character after string
		pop	si			;Restore registers
		pop	ds
		ret
write_command	endp

;------------------------------------------------------------------------------
; TOGGLE_INS toggles the insert	flag.
;------------------------------------------------------------------------------
toggle_ins	proc	near
		xor	cs:[insert_flag],1	;Toggle	insert flag
		call	set_cursor		;Set cursor mode
		ret
toggle_ins	endp

;------------------------------------------------------------------------------
; TAB tabs to the next tab boundary.
;------------------------------------------------------------------------------
tab		proc	near
		mov	al,cs:[bufferptr]	;Calculate number of
		dec	al			;  spaces to insert for
		xor	ah,ah			;  soft	tab
		mov	bl,8
		div	bl
		mov	cx,8
		sub	cl,ah
tab1:
		push	cx			;Print spaces
		mov	al,32
		call	printchar
		pop	cx
		jc	tab_exit
		loop	tab1
tab_exit:
		ret
tab		endp

;------------------------------------------------------------------------------
; BACKSPACE deletes the	character left of the cursor.
;------------------------------------------------------------------------------
backspace	proc	near
		cmp	cs:[bufferptr],1	;At beginning of command line?
		je	bs_exit			;Yes, then ignore it
		mov	cl,[si]			;Get count
		sub	cl,cs:[bufferptr]	;Calculate distance to end-of-line
		inc	cl
		xor	ch,ch
		push	cx			;Save it
		jcxz	bs1			;Branch	if at end-of-line
;
;Shift all characters right of the cursor in the buffer	one slot left.
;
		push	si			;Save SI and DI
		push	di
		mov	si,di			;Position them for shifts
		dec	di
		rep	movsb			;Shift characters right	of cursor
		pop	di			;Restore registers
		pop	si
;
;Display the new string	and update input parameters.
;
bs1:
		call	move_left		;Move cursor left
bs2:
		pop	cx			;Retrieve shift	count
		push	dx			;Save cursor position
		push	si			;Save SI
		mov	si,di			;Point SI to new part of string
		call	print_string		;Print the new part
		mov	ah,2			;Blank the last	character
		mov	dl,32
		int	21h
		pop	si			;Restore registers
		pop	dx			;Restore cursor	address
		mov	ah,2			;Reset the cursor
		int	10h
		dec	byte ptr [si]		;Decrement character count
bs_exit:
		ret
backspace	endp

;------------------------------------------------------------------------------
; PRINTCHAR writes a new character to the input	buffer and echoes it.
; Entry:  AL - character to print
; Exit:	  CF clear if character	printed
;	  CF set if buffer full
;------------------------------------------------------------------------------
printchar	proc	near
		cmp	cs:[insert_flag],0	;Insert	state on?
		jne	print3			;Yes, then branch
;
;Print a character in overstrike mode.
;
		mov	cl,[si]			;Get count
		cmp	cl,cs:[bufferptr]	;End-of-line?
		jae	print2			;No, then branch
		mov	cl,[si-1]		;Get maximum length
		sub	cl,[si]			;Subtract current length
		cmp	cl,1			;Buffer	full?
		je	beep			;Yes, then branch
print1:
		inc	byte ptr [si]		;Increment count
print2:
		stosb				;Deposit new character
		mov	ah,2			;Then print it
		mov	dl,al
		int	21h
		inc	cs:[bufferptr]		;Advance buffer	pointer
print_exit:
		clc				;Clear CF and exit
		ret
beep:
		mov	ax,0E07h		;Print ASCII 7 thru BIOS
		int	10h
		stc
		ret
;
;Print a character in insert mode.
;
print3:
		mov	cl,[si-1]		;Get maximum length
		sub	cl,[si]			;Subtract current count
		cmp	cl,1			;Buffer	full?
		je	beep			;Yes, then branch
		mov	cl,[si]			;Get count
		cmp	cl,cs:[bufferptr]	;End-of-line?
		jb	print1			;Yes, then branch
		sub	cl,cs:[bufferptr]	;Calculate number of shifts
		inc	cl
		xor	ch,ch
		push	cx			;Save shift count
		push	si			;Save SI
		add	di,cx			;Position DI to	end-of-line
		mov	si,di			;Position SI just before it
		dec	si
		std				;Set DF	for now
		rep	movsb			;Make room for new character
		cld				;Clear DF
		pop	si			;Restore SI
		mov	es:[di],al		;Deposit new character
		mov	ah,3			;Get cursor position
		int	10h
		pop	cx			;Retrieve shift	count
		inc	cx			;Increment it
		push	dx			;Save cursor position
		push	si			;Save SI
		mov	si,di			;Point SI to current location
		call	print_string		;Print new part	of string
		pop	si			;Restore SI and	DX
		pop	dx
		mov	ah,2			;Reset cursor position
		int	10h
		inc	byte ptr [si]		;Add to	character count
		call	move_right		;Move cursor right
		jmp	print_exit
printchar	endp

;------------------------------------------------------------------------------
; DELETE deletes the character at the cursor.
;------------------------------------------------------------------------------
delete		proc	near
		mov	cl,[si]			;Get count
		cmp	cl,cs:[bufferptr]	;End-of-line?
		jb	del2			;Yes, then ignore keypress
		sub	cl,cs:[bufferptr]	;Calculate number of shifts
		xor	ch,ch			;Byte to word in CX
		push	cx			;Save shift count
		jcxz	del1			;Branch	if no shifts
		push	si			;Save SI and DI
		push	di
		mov	si,di			;Position registers for	shift
		inc	si
		rep	movsb			;Shift chars right of cursor
		pop	di			;Restore registers
		pop	si
del1:
		mov	ah,3			;Get cursor position
		int	10h
		jmp	bs2			;Exit thru BACKSPACE routine
del2:
		ret
delete		endp

;------------------------------------------------------------------------------
; CTRL_BS deletes the word at the cursor.
;------------------------------------------------------------------------------
lesschars	dw	?			;Count of chars	deleted
shiftcount	dw	?			;Count of chars	shifted

ctrl_bs		proc	near
		mov	cl,[si]			;Exit now if there is nothing
		xor	ch,ch			;  on the command line
		or	cx,cx
		jnz	cbs1
cbs_exit:
		ret
cbs1:
		cmp	cs:[bufferptr],1	;Exit if the cursor is at the
		je	cbs3			;  at the end of the command
		cmp	cl,cs:[bufferptr]	;  line	or if it is under a
		jb	cbs_exit		;  space; otherwise, move to
		cmp	byte ptr [di],32	;  the beginning of the
		je	cbs_exit		;  current word
		cmp	byte ptr [di-1],32
		je	cbs3
cbs2:
		push	cx			;Save CX
		call	ctrl_left		;Move to start of word
		pop	cx			;Restore CX
cbs3:
		inc	cx			;Calculate max number of
		push	cx			;  characters to search
		mov	dl,cs:[bufferptr]	;  looking for the next
		xor	dh,dh			;  word	or end-of-line
		sub	cx,dx
		push	di			;Save DI
cbs4:
		inc	di			;Search	until DI addresses
		cmp	byte ptr [di],32	;  either the first character
		je	cbs5			;  in the next word or the
		cmp	byte ptr [di-1],32	;  end of the command line
		je	cbs6
cbs5:
		loop	cbs4
cbs6:
		mov	dx,di			;Save final value of DI
		pop	di			;Restore DI
		pop	cx			;Retrieve count
		mov	cs:[lesschars],dx	;Calculate number of chars to
		sub	cs:[lesschars],di	;  be deleted
		sub	cx,dx			;Then calculate	how many chars
		add	cx,si			;  must	be shifted
		mov	cs:[shiftcount],cx
		jcxz	cbs7			;Branch	if no shift
		push	si			;Save registers
		push	di
		mov	si,dx			;Point DS:SI to	next word
		rep	movsb			;Delete	current	word
		pop	di			;Restore registers
		pop	si
cbs7:
		mov	cx,cs:[lesschars]	;Update	character counter
		sub	[si],cl			;  in input buffer
		mov	ah,3			;Save the current cursor
		int	10h			;  position on the stack
		push	dx
		push	si			;Update	the text on the
		mov	si,di			;  command line
		mov	cx,cs:[shiftcount]
		call	print_string
		pop	si
		mov	cx,cs:[lesschars]
cbs8:
		mov	ah,2			;Print as many spaces as
		mov	dl,32			;  there were characters
		int	21h			;  deleted
		loop	cbs8
		pop	dx			;Restore cursor	position
		mov	ah,2			;  and exit
		int	10h
		ret
ctrl_bs		endp

;------------------------------------------------------------------------------
; CTRL_END deletes command line	text from the cursor to	the end	of the line.
;------------------------------------------------------------------------------
ctrl_end	proc	near
		mov	cl,[si]			;Exit if already at end
		cmp	cl,cs:[bufferptr]	;  of line
		jb	ce_exit
		sub	cl,cs:[bufferptr]	;Calculate number of chars
		xor	ch,ch			;  to be deleted
		inc	cx
		sub	[si],cl			;Update	count in input buffer
		push	cx
		mov	ah,3			;Get and save cursor position
		int	10h
		pop	cx
		push	dx
ce1:
		mov	ah,2			;Print as many spaces as
		mov	dl,32			;  there are characters
		int	21h			;  to delete
		loop	ce1
		mov	ah,2			;Reset cursor position and
		pop	dx			;  exit
		int	10h
ce_exit:
		ret
ctrl_end	endp

;------------------------------------------------------------------------------
; MOVE_LEFT moves the cursor one character left.
;------------------------------------------------------------------------------
move_left	proc	near
		cmp	cs:[bufferptr],1	;At beginning of line?
		je	left2			;Yes, then ignore keypress
		mov	ah,3			;Get cursor position
		int	10h
		dec	dl			;Decrement it by 1
		cmp	dl,0FFh
		jne	left1
		mov	dl,cs:[columns]		;Decrement row number by 1
		dec	dh			;  if cursor wraps around
left1:
		mov	ah,2			;Set new position
		int	10h
		dec	di			;Decrement pointers
		dec	cs:[bufferptr]
left2:
		ret
move_left	endp

;------------------------------------------------------------------------------
; MOVE_RIGHT moves the cursor one character right.
;------------------------------------------------------------------------------
move_right	proc	near
		mov	cl,[si]			;Get count
		cmp	cl,cs:[bufferptr]	;End-of-line?
		jb	rt2			;Yes, then ignore keypress
		mov	ah,3			;Get cursor position
		int	10h
		inc	dl			;Increment column number
		cmp	dl,cs:[columns]		;Increment row number if
		jna	rt1			;  cursor wraps	around
		xor	dl,dl
		inc	dh
rt1:
		mov	ah,2			;Position cursor
		int	10h
		inc	di			;Advance pointers
		inc	cs:[bufferptr]
rt2:
		ret
move_right	endp

;------------------------------------------------------------------------------
; CTRL_LEFT moves the cursor one word left.
;------------------------------------------------------------------------------
ctrl_left	proc	near
		call	move_left		;Move one character left
		cmp	cs:[bufferptr],1	;Beginning of line?
		je	cl_exit			;Yes, then exit
		cmp	byte ptr es:[di],32	;Loop back if current char
		je	ctrl_left		;  is a	space
		cmp	byte ptr es:[di-1],32	;Loop back if char to the
		jne	ctrl_left		;  left	is not a space
cl_exit:
		ret
ctrl_left	endp

;------------------------------------------------------------------------------
; CTRL_RIGHT moves the cursor one word right.
;------------------------------------------------------------------------------
ctrl_right	proc	near
		call	move_right		;Move one character right
		mov	cl,[si]			;End-of-line?
		cmp	cl,cs:[bufferptr]	;Yes, then exit
		jb	cr_exit
		cmp	byte ptr es:[di],32	;Loop back if current char
		je	ctrl_right		;  is a	space
		cmp	byte ptr es:[di-1],32	;Loop back if char to the
		jne	ctrl_right		;  left	is not a space
cr_exit:
		ret
ctrl_right	endp

;------------------------------------------------------------------------------
; HOME relocates the cursor to the beginning of	the command line.
;------------------------------------------------------------------------------
home		proc	near
		mov	cl,cs:[bufferptr]	;Get position pointer
		dec	cl			;Calculate distance from start
		xor	ch,ch
		jcxz	home_exit		;Exit if already there
home1:
		push	cx			;Save count
		call	move_left		;Move left one space
		pop	cx			;Retrieve count
		loop	home1			;Loop until done
home_exit:
		ret
home		endp

;------------------------------------------------------------------------------
; EOL advances the cursor to the end of	the command line.
;------------------------------------------------------------------------------
eol		proc	near
		mov	cl,[si]			;Get count
		cmp	cl,cs:[bufferptr]	;Already at end?
		jb	eol_exit		;Yes, then exit
		sub	cl,cs:[bufferptr]	;Calculate distance from end
		inc	cl
		xor	ch,ch			;Byte to word in CX
eol1:
		push	cx			;Advance right CX times
		call	move_right
		pop	cx
		loop	eol1
eol_exit:
		ret
eol		endp

;------------------------------------------------------------------------------
; CLEAR_LINE clears the	command	line.
; Entry:  BH - current video page
;------------------------------------------------------------------------------
clear_line	proc	near
		mov	cl,[si]			;Get count
		xor	ch,ch
		jcxz	cline2			;Exit if no characters
		push	cx			;Save count
		call	home			;Home the cursor
		mov	ah,3			;Get cursor position
		int	10h
		pop	cx			;Restore CX
		push	dx			;Save cursor address
		mov	ah,2			;Print ASCII spaces
		mov	dl,32
cline1:
		int	21h
		loop	cline1
		mov	ah,2			;Home cursor again
		pop	dx
		int	10h
		mov	byte ptr [si],0		;Reset count
cline2:
		ret
clear_line	endp

;------------------------------------------------------------------------------
; CMD_RECORD records the latest	command	in the command stack.
; Entry:  DS:DX	- input	buffer address
;------------------------------------------------------------------------------
cmd_record	proc	near
		mov	si,dx			;Point SI to input buffer
		mov	al,[si+1]		;Get length of input string
		cmp	al,cs:[minlength]	;Is the	length zero?
		jb	cmd_exit		;Yes, then exit	now
		inc	si			;Advance SI to count byte
		push	cs			;Point ES to command stack
		pop	es			;  segment
		mov	di,cs:[next_ptr]	;Get command stack pointer
		mov	cl,7			;Convert it to offset address
		shl	di,cl
		add	di,cs:[cmdstack_base]
		mov	cl,al			;Transfer string length	to CL
		inc	cl			;Increment for count byte
		xor	ch,ch			;Byte to word in CX
		cld				;Clear DF
		rep	movsb			;Copy string to	command	stack
		inc	cs:[next_ptr]		;Update	pointer
		mov	ax,cs:[cmdstack_size]
		cmp	cs:[next_ptr],ax	;Wrap around if	necessary
		jne	cmdrec1
		mov	cs:[next_ptr],0
cmdrec1:
		mov	cs:[cmd_ptr],0		;Zero current command pointer
cmd_exit:
		ret
cmd_record	endp

;------------------------------------------------------------------------------
; SET_CURSOR sets the cursor mode based	on the state of	the insert flag.
;------------------------------------------------------------------------------
set_cursor	proc	near
		cmp	cs:[insert_flag],0	;Test state of insert flag
		je	setc1			;Branch	if not set
		mov	cx,cs:[cursor_mode]	;Raise top by 2	scan lines
		sub	ch,2
		jns	setc2			;Exit if it wraps around
		ret
setc1:
		mov	cx,cs:[cursor_mode]	;Set default cursor
setc2:
		mov	ah,1			;Set cursor mode
		int	10h
		ret
set_cursor	endp

;============================================================================
; MUXINT processes calls to interrupt 2Fh
; Entry:  AH - Device ID
; Exit:	  AL - 0FFh if AH = Alias device ID. Unchanged otherwise.
;	  ES - Code segment if AH = Alias device ID. Unchanged otherwise.
;============================================================================
muxint		proc	far
		assume	cs:code,ds:nothing,es:nothing
		cmp	ah,cs:[multiplex_id]	;Check for program ID
		je	muxint_1		;Its us, indicate installed.
		jmp	cs:[int2fh]		;else pass the call on
muxint_1:
		mov	al,-1			;Indicate Alias	installed
		push	cs			;ES = installed	code segment
		pop	es
		iret
muxint		endp

		even				;Align stack on	word boundry
end_of_resident	=	$
;----------------------------------------------------------------------------
; Start	of non-resident	code.
;----------------------------------------------------------------------------
final_install:
		rep	movsb			;Copy alias list
		mov	di,cmdstack_base	;Initialize command stack
		mov	cx,cmdstack_size	;  area	with leading zeroes
		jcxz	tsr			;Branch	if size	is zero
		xor	al,al
		cld
final_loop:
		stosb
		add	di,127
		loop	final_loop
tsr:
		mov	ax,3100h		;Terminate and stay resident
		int	21h

;----------------------------------------------------------------------------
; Non-resident data.
;----------------------------------------------------------------------------
alrdy_installed	db	0			;Installed flag
other_seg	dw	0			;Segment of installed code
databuff_seg	dw	0			;Segment of data buffer.

alias_buffer	dw	512			;Extra buffer for alias	list.
aliaslist_end	dw	0			;Offset	of end of the list.
alias_inlist	db	0			;Flag used in alias list append

infomsg1	db	"ALIAS uninstalled$"
infomsg2	db	13,10,9,"Command stack size: $"
infomsg3	db	13,10,9,"Minimum stacked command length: $"
infomsg4	db	13,10,9,"Bytes free in alias buffer: $"
infomsg5	db	13,10,9,"Alias translation is $"
infomsg5d	db	"disabled",13,10,"$"
infomsg5e	db	"enabled",13,10,"$"
infomsg6	db	13,10,9,"FUNCTION KEY DEFINITIONS$"
infomsg7	db	13,10,9,"ALIAS DEFINITIONS$"
infomsg8	db	13,10,"For help type ALIAS ?$"
infomsg9	db	13,10,"Can",39,"t find Master Env. block$"

filemsg1	db	13,10,"Error in line $"	;File identification message.
filemsg2	db	" of file: "
filenam_field	db	78 dup (0)		;Name of current entry file.

errmsg0		db	"Need DOS 2.0 or greater$"
errmsg1		db	"ALIAS not installed$"

errmsg2		db	13,10,"Syntax: ALIAS [alias [command]] "
		db	"[/F filename] [/S nn] [/U]",13,10
		db	14 dup (" ")
		db	"[/B nn] [/D] [/E] [/L] [/M]",13,10,10
		db	9,"ALIAS alias command",13,10
		db	9,"ALIAS [fn] command",13,10,10
		db	9,"/B = Buffer size",13,10
		db	9,"/D = Disable alias translation",13,10
		db	9,"/E = Enable alias translation",13,10
		db	9,"/F = Filename of command file",13,10
		db	9,"/L = List aliases",13,10
		db	9,"/M = Minimum command length to stack",13,10
		db	9,"/S = Size of command stack",13,10
		db	9,"/U = Uninstall",13,10,"$"

errmsg3		db	"Can",39,"t uninstall$"
errmsg4		db	"Can",39,"t change parameter after installation$"
errmsg5		db	"Illegal number$"
errmsg6		db	"Can",39,"t find alias file$"
errmsg7		db	"Can",39,"t find COMMAND.COM$"
errmsg8		db	"Not enough memory$"
errmsg9		db	"Alias list full$"
errmsg10	db	"List and stack too large$"
errmsg11	db	"Invalid key assignment$"
errmsg12	db	"Number too big$"
errmsg13	db	"Alias not in list$"
errmsg14	db	"Error using Int 2Fh$"
endmsg		db	13,10,"$"

shiftmsg	db	"S-$"
altmsg		db	"A-$"
ctlmsg		db	"C-$"

file_linecount	dw	0			;Line number being processed.
caps_flag	db	0
param_found	db	0			;Cmd line parameter found flag
append_cr	db	0			;Append	cr to alias flag
cmdcom_name	db	"COMMAND"		;Name of command.com

cmd_switches	db	"sfeldbum*/"		;Letters of valid commands.
cmd_switch_end	=	$
cmd_jmp_tbl	dw	offset setstacksize	;This jump table is used to
		dw	offset loadaliasfile	;  call	the routines that
		dw	offset enablealias	;  process the command line
		dw	offset listalias	;  arguments
		dw	offset disablealias
		dw	offset setlistbuffer
		dw	offset remove
		dw	offset minstacklen
		dw	offset comment_line	;Comments can be indicated by
		dw	offset comment_line	;  a /*	or a //.

;----------------------------------------------------------------------------
; Initialization routine.
;----------------------------------------------------------------------------
initialize	proc	near
		assume	cs:code,ds:code,es:code
		cld
		mov	ah,30h			;Get DOS version
		int	21h
		xchg	al,ah			;Swap major, minor numbers
		mov	dx,offset errmsg0	;Bad DOS version
		cmp	ah,2			;Run if	DOS 2.0	or greater.
		jb	jmp_disp_error
		mov	dos_version,ax		;Save version number

		mov	ax,max_code_size	;Get size of installed code.
		mov	bx,ax
		shl	ax,1			;Convert to bytes
		shl	ax,1
		shl	ax,1
		shl	ax,1
		mov	sp,ax			;Move stack pointer
		sub	ax,512			;Save room for stack
		mov	aliaslist_size,ax	;Mark end of alias list
		mov	ah,4ah			;Reduce	memory allocation
		int	21h

		mov	bx,filebuf_size		;Get size of file buffer,
		mov	ah,48h			;Allocate memory block
		int	21h
		mov	dx,offset errmsg8	;Not enough memory msg
		jc	jmp_disp_error
		mov	databuff_seg,ax		;Save segment to file buffer.

		mov	ax,offset end_of_code		;Initialize alias list
		mov	word ptr [aliaslist_ptr],ax	;  pointers.
		mov	ax,cs
		mov	word ptr [aliaslist_ptr+2],ax
		les	di,aliaslist_ptr		;Initialize alias list
		mov	ax,-1				;  by writing a	-1 as
		stosw					;  an end flag.
		mov	aliaslist_end,di
;
;See if	a copy is already resident in memory. If > DOS 3.0, use	int 2Fh.
;
		mov	byte ptr [main+2],0	;Initialize fingerprint
		cmp	dos_version,300h	;See if	DOS 3.0	or later
		jb	find_copy1		;No, search the	old way.
		mov	cx,16			;Try 16	different IDs.
find_copy:
		xor	ax,ax
		mov	es,ax
		mov	ah,multiplex_id		;Load ID.  Use Int 2Fh to
		int	2fh			;  reach installed code	so
		or	al,al			;  that	we are compatible
		jne	find_copy0		;  with	386 memory managers.
		push	cs
		pop	es			;If AL not changed, ALIAS not
		jmp	short find_copy4	;  installed.
find_copy0:
		push	cx
		call	cmpheader		;See if	really Alias by
		pop	cx			;  comparing file headers.
		je	find_copy3
		inc	multiplex_id		;ID used by another program.
		loop	find_copy		;  Change and try again.
		mov	dx,offset errmsg14	;All IDs taken,	print error
jmp_disp_error:
		jmp	disp_error		;  msg and exit.
;
;For DOS 2.x find the installed	code the old fashioned way by scanning
;the memory control blocks.
;
find_copy1:
		xor	bx,bx			;zero BX for start
		mov	ax,cs			;keep CS value in AX
find_copy2:
		inc	bx			;increment search segment value
		mov	es,bx
		assume	es:nothing
		cmp	ax,bx			;not installed if current
		je	find_copy4		;  segment is found.
		call	cmpheader
		jne	find_copy2		;loop back if not found
find_copy3:
		inc	alrdy_installed		;Set installed flag
find_copy4:
		mov	other_seg,es		;Save seg of installed code
;
;Copy the command line to the file buffer to treat as a	one line file.
;
		mov	ax,databuff_seg		;Get segment of	file buffer.
		mov	es,ax			;Treat the command line	as
		xor	di,di			;  a 1 line file.
		mov	si,offset command_tail
		xor	cx,cx
		or	cl,[si]			;Get command line length
		je	parse_line_end		;If zero, skip parse routine
		inc	si			;Copy command line into	file
		inc	cx			;  buffer.
		push	cx
		rep	movsb
		pop	cx			;CX = file size.
		xor	si,si			;Point SI to start of buffer
		push	es
		pop	ds			;Set DS	to file	buffer seg
		assume	ds:nothing
		mov	es,cs:[other_seg]	;Set ES	to installed code
;
;Parse command line and	command	files.
;
parse_line_loop:
		xor	bl,bl
		call	scanline		;Get 1st character
		jc	parse_2			;If carriage return, skip line
		cmp	al,"/"			;Compare character to switch.
		je	parse_switch_found
		cmp	al,"?"			;See if	help character
		mov	dx,offset errmsg2	;Command not found msg
		je	disp_error
		call	setkey			;Must be alias definition
		jc	disp_error
		mov	param_found,1		;Set parameter found flag
		jmp	short parse_2		;Return	to parse loop
parse_switch_found:
		mov	param_found,1		;Set parameter found flag
		lodsb				;Get command line switch
		dec	cx
		cmp	al,'A'			;Convert to lower case if
		jb	parse_1			;  necessary.
		cmp	al,'Z'
		ja	parse_1
		or	al,20h
parse_1:
		push	cx			;Scan the list of allowable
		push	es			;  command switches. If	switch
		push	cs			;  found, use its position for
		pop	es			;  an index into the list of
		mov	di,offset cmd_switches	;  routines.
		mov	cx,offset cmd_switch_end - offset cmd_switches
		mov	bx,offset cmd_switch_end - offset cmd_switches - 1
		repne	scasb
		mov	ax,cx			;Copy index into list
		pop	es
		pop	cx
		mov	dx,offset errmsg2	;Command not found msg
		jne	disp_error
		sub	bx,ax			;Compute offset	into list
		shl	bx,1			;Convert to word offset
		add	bx,offset cmd_jmp_tbl	;Add the start addr of table.
		call	cs:[bx]			;Call command routine.
		jc	disp_error		;If error terminate parse.
parse_2:
		jcxz	parse_line_end		;If at file end, exit parse.
		jmp	short parse_line_loop
;
;See if	installed. If not, install.
;
parse_line_end:
		cmp	cs:[param_found],1	;See if	any parameters found
		je	init2			;If already installed and
		cmp	cs:[alrdy_installed],0	;  no parameters on the
		je	init2			;  command line, default to
		mov	es,cs:[other_seg]	;  showing program information
		call	listalias
init2:
		mov	ah,49h			;Release memory	block used
		mov	es,cs:[databuff_seg]	;  for file buffer.
		int	21h
		push	cs
		pop	ds
		assume	ds:code
		mov	word ptr databuff_seg,0	;Indicate file buff released
		cmp	alrdy_installed,0	;If not	already	installed,
		je	install			;  jump	to install routine.
exit:
		mov	ax,4C00h		;Terminate with	RC = 0
		int	21h
;
;Display error message.
;
		assume	ds:nothing
disp_error:
		xor	ax,ax			;If file buffer	still
		or	ax,databuff_seg		;  allocated, deallocate it.
		jz	disp_error0
		mov	ah,49h			;Release memory	block used
		mov	es,ax			;  for file buffer.
		int	21h
disp_error0:
		push	cs
		pop	ds
		assume	ds:code
		cmp	byte ptr filenam_field,0
		je	disp_error1		;If processing a file, print
		push	dx			;  a message informing the
		mov	dx,offset filemsg1	;  user	the filename being
		call	printmsg		;  processed and the line
		mov	ax,file_linecount	;  that	the error occured.
		call	hex2asc
		mov	dx,offset filemsg2
		call	printmsgcr
		pop	dx
disp_error1:
		call	printmsgcr		;print string
		mov	ax,4c01h		;Terminate with	RC = 1
		int	21h
;
;Install routine. Find segment of COMMAND.COM, hook into int 21h, 2Fh and TSR.
;
		assume	ds:code
install:
		mov	dx,offset program	;Display copyright message
		call	printmsg

		push	ds			;Save DS
		xor	ax,ax			;Then zero it
		mov	ds,ax
		mov	cs:[points],ax		;Record	number of scan lines
		mov	ax,ds:[0460h]		;  per character and cursor
		mov	cs:[cursor_mode],ax	;  mode
		pop	ds			;Restore DS

		mov	byte ptr filenam_field,0  ;Don't callout file on error
		mov	ah,52h			;get address of	first MCB
		int	21h
		mov	ax,es:[bx-2]		;point ES to MCB
		mov	cx,20			;Allow only 20 loops.
mcb_loop:
		mov	es,ax
		cmp	byte ptr es:[0],"M"	;check for mcb signature
		jne	mcb_error
		inc	ax			;point AX to memory block
		cmp	ax,es:[1]		;See if	this is	a PSP block
		je	psp_found
mcb_continue:
		add	ax,es:[3]		;Get size of memory block
		loop	mcb_loop
mcb_error:
		mov	dx,offset errmsg7	;Can't locate command.com PSP
		jmp	disp_error
psp_found:
		cmp	dos_version,0a00h	;If OS/2, use DOS 3.3 method.
		jae	psp_found_1
		cmp	dos_version,0400h	;If DOS	4.00 or	greater,
		jb	psp_found_1		;  COMMAND.COM may not be the
		push	ds			;  first program loaded.  Look
		mov	si,offset cmdcom_name	;  at the name of the program
		mov	di,8			;  stored in the last 8	bytes
		mov	cx,7			;  of the memory control
		repe	cmpsb			;  block.  If the string
		pop	ds			;  "COMMAND" isn't found, keep
		jne	mcb_continue		;  looking.
psp_found_1:
		mov	cmdcom_psp,ax		;Save segment of cmd.com PSP
		mov	es,ax
		mov	bx,es:[2ch]		;Get seg of master environment
		mov	master_env,bx
		dec	bx
		mov	es,bx
		cmp	byte ptr es:[0],"M"	;See if	valid memory block
		je	psp_found_4
		mov	dx,ax			;Save COMMAND PSP segment
		dec	ax			;Point back to mcb
		mov	es,ax
psp_found_2:
		add	ax,es:[3]		;Get size of memory block
		inc	ax
		cmp	es:[1],dx		;See if	owned by CMD.COM
		je	psp_found_3
		mov	es,ax
		loop	psp_found_2
		mov	dx,offset infomsg9	;Indicate can't find env
		call	printmsgcr
		xor	ax,ax
psp_found_3:
		inc	ax
		mov	master_env,ax
psp_found_4:
;
;Set up	pointers to the	cmd stack and to move the alias	list to	the end	of the
;command stack so it takes up less space when ALIAS resident.
;
		mov	di,mystack_ptr		;Get base of internal stack
		mov	work_buff_ptr,di	;Copy ptr to alias buffer
		add	di,128			;Add size of buffer
		mov	cmdstack_base,di	;Copy for start	of cmd stack
		mov	ax,cmdstack_size	;Get size of command stack
		mov	ah,128			;128 bytes for each entry
		mul	ah
		add	di,ax				;Add to	cmd stack for
		mov	si,word	ptr aliaslist_ptr	;  start of the
		mov	cx,aliaslist_end		;  alias list.
		mov	word ptr aliaslist_ptr,di	;Save new pointer.
		sub	cx,si			;Compute size of list.
		mov	dx,di			;See if	we have	enough room
		add	dx,cx			;  for everything in one seg.
		jnc	install_1
		mov	dx,offset errmsg10	;Not enough memory, exit.
		jmp	disp_error
install_1:
		add	dx,alias_buffer		;Add additional	space for list
		mov	aliaslist_size,dx	;Save pointer to end of	seg
		add	dx,15			;Convert memory	needed to
		shr	dx,1			;  paragraphs.
		shr	dx,1
		shr	dx,1
		shr	dx,1
		cmp	di,si			;Check for overlap in the move
		jb	install_2		;If overlap, copy list from
		std				;  the top down	to avoid
		add	di,cx			;  overwriting the list.
		add	si,cx
		dec	si
		dec	di
;
;Revector interrupts 21h and 2Fh (if necessary).
;
install_2:
		mov	ax,3521h		;Get interrupt 21 (DOS)
		int	21h			;  vector.
		mov	word ptr [int21h],bx
		mov	word ptr [int21h+2],es
		push	dx			;Save memory size parameter
		mov	ax,2521h		;Point int 21 to internal
		mov	dx,offset dosint	;  routine.
		int	21h

		cmp	dos_version,300h	;See if	we are using Int 2Fh
		jb	install_3
		mov	ax,352fh		;Get interrupt 2F (MUX)
		int	21h			;  vector.
		mov	word ptr [int2fh],bx
		mov	word ptr [int2fh+2],es
		mov	ax,252fh		;Point int 2F to internal
		mov	dx,offset muxint	;  routine.
		int	21h
install_3:
		pop	dx
		push	ds			;ES = DS
		pop	es
		jmp	final_install		;Jump to safe place for	move.
initialize	endp

;-----------------------------------------------------------------------------
; CMPHEADER compares the first 16 bytes	of this	file with the segment
;	    pointed to by ES.
; Entry:  DS - code segment
;	  ES - pointer to segment to compare
; Exit:	  ZF - 0 = segments match.
;-----------------------------------------------------------------------------
cmpheader	proc	near
		mov	si,offset main+2	;Search	this segment for ASCII
		mov	di,si			;  fingerprint.
		mov	cx,16
		repe	cmpsb
		ret
cmpheader	endp

;-----------------------------------------------------------------------------
; PRINTMSG prints the message pointed to by DX to the screen.
; Entry:  DX - pointer to ASCII	message	terminated by $
;-----------------------------------------------------------------------------
printmsg	proc	near
		assume	ds:nothing,es:nothing
		push	ds
		push	cs
		pop	ds
		assume	ds:code
		mov	ah,9			;Print message
		int	21h
		pop	ds
		ret
printmsg	endp

;-----------------------------------------------------------------------------
; PRINTMSGCR calls PRINTMSG, then appends a carriage return to the message.
; Entry:  DX - pointer to ASCII	message	terminated by $
;-----------------------------------------------------------------------------
printmsgcr	proc	near
		assume	ds:nothing,es:nothing
		push	dx
		call	printmsg
		mov	dx,offset endmsg
		call	printmsg
		pop	dx
		ret
printmsgcr	endp

;-----------------------------------------------------------------------------
; SETSTACKSIZE - Sets the size of the command line stack.
; Entry:  DS:SI	- points to stack size in ascii
; Exit:	     CF	- Clear	if sucessful, Set if error, DX points to error msg.
;-----------------------------------------------------------------------------
setstacksize	proc	near
		assume	ds:nothing,es:nothing
		xor	bl,bl			;Check for installed code
		call	setparameter		;Get num and convert to	binary
		jc	setstack_exit		;Check for error
		mov	cs:[cmdstack_size],ax	;Save parameter
setstack_exit:
		ret
setstacksize	endp

;-----------------------------------------------------------------------------
; SETLISTBUFFER	- Sets the size	of the additional buffer reserved for alias
;		  list expansion.
; Entry:  DS:SI	- points to buffer size	in ascii
; Exit:	     CF	- Clear	if sucessful, Set if error, DX points to error msg.
;-----------------------------------------------------------------------------
setlistbuffer	proc	near
		assume	ds:nothing,es:nothing
		xor	bl,bl			;Check for installed code
		call	setparameter		;Get num and convert to	binary
		jc	setlistbuffer_exit	;Check for error
		mov	cs:[alias_buffer],ax	;Save buffer size parameter
setlistbuffer_exit:
		ret
setlistbuffer	endp

;-----------------------------------------------------------------------------
; MINSTACKLEN -	sets the minimum legth of a command to stack.
; Entry:     ES	- segment of the installed code
;	  DS:SI	- points to buffer size	in ascii
; Exit:	     CF	- Clear	if sucessful, Set if error, DX points to error msg.
;-----------------------------------------------------------------------------
minstacklen	proc	near
		assume	ds:nothing,es:nothing
		mov	bl,1			;Don't check for installed code
		call	asc2bin			;Get num and convert to	binary
		jc	minstacklen_exit	;Check for error
		cmp	al,126
		jb	minstacklen_1
		mov	dx,offset errmsg12	;Stack length too big
		stc
		jmp	short minstacklen_exit
minstacklen_1:
		or	al,al			;Make sure min length is not
		jne	minstacklen_2		; specified at 0. If so,
		inc	al			; change to 1.
minstacklen_2:
		mov	es:[minlength],al	;Save minimum length parameter
minstacklen_exit:
		ret
minstacklen	endp

;-----------------------------------------------------------------------------
; SETPARAMETER - Common	code used by the set stack and set buffer and set
;		 minimum command length	routines.
; Entry:  DS:SI	- points to ascii number
;	     BL	- Flag to indicate check for installed code, BL=0, check.
; Exit:	     CF	- Clear	if sucessful, Set if error.
;-----------------------------------------------------------------------------
setparameter	proc	near
		assume	ds:nothing,es:nothing
		mov	dx,offset errmsg4	;Can't change parameter msg
		cmp	cs:[alrdy_installed],1	;If already installed don't
		je	setparam_error		;  change parameter.
		call	asc2bin
setparam_exit:
		ret
setparam_error:
		stc				;Set error flag.
		jmp	short setparam_exit
setparameter	endp

;-----------------------------------------------------------------------------
; SETKEY modifies the alias list to add	function key definitions.
; Entry:  DS:SI	- pointer to string identifing the function key
;	     ES	- pointer to segment of	installed code
; Exit:	     CF	- clear	if successful
;-----------------------------------------------------------------------------
setkey		proc	near
		assume	ds:nothing,es:nothing
		push	es
		cmp	al,"["			;Determine alias or key
		je	setkey_1
		dec	si			;Backup	before last key
		inc	cx
		jmp	short setkey_5
setkey_1:
		lodsb				;Get next character
		dec	cx
		jcxz	setkey_badkey
		mov	bx,58			;Load default F1 keycode
		or	al,20h			;Convert to lower case
		cmp	al,"f"
		je	setkey_3
		add	bx,25			;Assume	shift F1 keycode
		cmp	al,"s"
		je	setkey_2
		add	bx,10			;Assume	ctl F1 keycode
		cmp	al,"c"
		je	setkey_2
		add	bx,10			;Assume	alt F1 keycode
		cmp	al,"a"
		jne	setkey_badkey
setkey_2:
		lodsb				;Get next character
		dec	cx
		or	al,20h			;Convert to lower case
		cmp	al,"f"			;Make sure next	key is "f"
		jne	setkey_badkey
setkey_3:
		call	asc2bin
		jc	setkey_badkey		;If bad	number,	exit
		or	ax,ax
		je	setkey_badkey		;Make sure not zero
		cmp	ax,10
		ja	setkey_badkey		;Make sure less	than 10
		cmp	byte ptr [si-1],"]"	;Check for closing bracket
		jne	setkey_badkey		;  if not found, error
		add	bx,ax			;Add in	function key number
		cmp	byte ptr [si]," "	;Check to see if a character
		jbe	setkey_4		;  trails the closing bracket.
		cmp	byte ptr [si],"*"	;Check to see if an * is
		jne	setkey_badkey		;  appended to the function
		lodsb				;  key assignment. If so,
		dec	cx			;  remove key and set flag
		mov	cs:[append_cr],1	;  to append cr	to command.
setkey_4:
		sub	si,2			;Back up to keycode.
		xor	bh,bh
		mov	ds:[si],bx		;Save keycode on command line
		add	cx,2
setkey_5:
		call	setalias		;Use SETALIAS to load
setkey_exit:
		pop	es			;Restore ptr to	installed seg
		ret
setkey_badkey:
		mov	dx,offset errmsg11	;Bad key assignment msg
		stc
		jmp	setkey_exit
setkey		endp

;-----------------------------------------------------------------------------
; SETALIAS modifies the	alias list according to	command	line agruments.
; Entry:  DS:SI	- pointer to string to be inserted into	alias list.
;	     ES	- pointer to segment of	installed code.
; Exit:	     CF	- clear	if successful
;-----------------------------------------------------------------------------
setalias	proc	near
		assume	ds:nothing,es:nothing
		push	es
		xor	bl,bl			;Find 1st character on
		call	scanline		;  command line.
		jnc	setalias_1		;If at end of line, exit
		jmp	setalias_exit		;  routine.
setalias_1:
;
;Get length of alias, then search list for matching alias
;
		dec	si			;Backup	to before 1st char.
		inc	cx
		mov	byte ptr cs:[alias_inlist],0 ;Assume not in list
		call	searchalias		;Is there already an alias?
		jc	setalias_2		;No, continue.

		inc	byte ptr cs:[alias_inlist]
		call	delete_aliasent		;Delete	entry from list
setalias_2:
;
;Append	new alias to the end of	the list.
;
		mov	bp,di			;Save ptr to end of list.
		add	di,4			;Move past length fields.
		push	es			;Get max size of alias list.
		mov	es,cs:[other_seg]
		mov	dx,es:[aliaslist_size]
		pop	es
;
;Append	alias to list.
;
		xor	ax,ax			;Clear character counter
setalias_3:
		lodsb				;Get byte
		dec	cx			;Decriment buffer counter.
		jcxz	setalias_4		;If at end of file, exit.
		cmp	al,13			;See if	at end of line.
		jne	setalias_6		;No, continue.
setalias_4:
		cmp	byte ptr cs:[alias_inlist],0
		jne	setalias_5		;Was alias in list?
		jmp	setalias_notnfil	;No, incomplete	alias specifed
setalias_5:
		jmp	setalias_exit		;Yes, alias simply erased.
setalias_6:
		cmp	al,' '			;See if	at end of tag.
		je	setalias_8		;Yes, exit copy	loop
		cmp	al,9			;Check for tab
		je	setalias_8
		cmp	di,dx			;See if	alias list is full
		jbe	setalias_7		;No, continue
		jmp	setalias_full		;Yes, exit routine
setalias_7:
		stosb				;No, add character to list
		inc	ah			;Inc size of tag
		jmp	short setalias_3
setalias_8:
		mov	es:[bp+2],ah		;Save size of alias
;
;Append	command	to alias list.
;
		mov	cs:[caps_flag],0	;Initialize setcaps flag.
		xor	bx,bx			;Find 1st character in
		call	scanline		;  command.
		jc	setalias_exit		;If no command,	exit
		xor	ah,ah			;Clear character counter
setalias_9:
		cmp	al,"%"			;Check for environment var
		jne	setalias_11		;  by checking for % signs.
		cmp	cs:[caps_flag],0	;If caps flag set capitialize
		jne	setalias_10		;  string before saving.
		mov	bl,ds:[si]		;Get next character
		cmp	bl,"0"			;If numeric, assume this is
		jb	setalias_10		;  a line parameter, not an
		cmp	bl,"9"			;  environment variable	so
		jbe	setalias_11		;  don't set caps flag.
		cmp	bl,"%"			;Don't let double % signs
		je	setalias_11		;  indicate environment	var.
setalias_10:
		not	byte ptr cs:[caps_flag]	;Toggle	caps flag
setalias_11:
		cmp	cs:[caps_flag],0	;Capitialize environment
		je	setalias_12		;  variables so	they will
		cmp	al,"a"			;  match when searched for in
		jb	setalias_12		;  the environment block.
		cmp	al,"z"
		ja	setalias_12
		and	al,0dfh			;Make character	uppercase.
setalias_12:
		cmp	di,dx			;See if	alias list is full
		ja	setalias_full		;Yes, exit routine
		stosb				;Append	character on list
		inc	ah			;Inc character counter.
		jcxz	setalias_13		;Check for end of file.
		lodsb				;Get next character
		dec	cx			;Dec file counter.
		cmp	al,13			;See if	carriage return.
		jne	setalias_9		;If not	continue
setalias_13:
		cmp	cs:[append_cr],1	;If flag set, append carrage
		jne	setalias_14		;  return to command.
		mov	al,13
		inc	ah
		stosb
		mov	cs:[append_cr],0	;Clear flag
setalias_14:
		mov	es:[bp+3],ah		;Save command size
		mov	word ptr es:[di],-1	;Set new end of	list flag
		mov	ax,di			;Save end pointer to list
		add	ax,2			;Make room for the end flag.
		mov	cs:[aliaslist_end],ax
		sub	di,bp			;Compute size of entry
		mov	es:[bp],di		;Put size over old end flag.
		inc	cs:[file_linecount]	;Point counter to next line.
setalias_exit:
		clc
setalias_exit1:
		pop	es			;Restore ptr to	installed seg
		ret
setalias_full:
		mov	dx,offset errmsg9	;Alias list too	large msg.
		stc
		jmp	short setalias_exit1
setalias_notnfil:
		mov	dx,offset errmsg13	;Alias not in list.
		stc
		jmp	short setalias_exit1
setalias	endp

;-----------------------------------------------------------------------------
; DELALIASENTRY	- Deletes an alias entry
; Entry:  DS:SI	- pointer to the entry in the alias list to delete
; Exit:	     CF	- clear	if successful
;-----------------------------------------------------------------------------
delete_aliasent	proc	near
		push	cx			;Save registers
		push	si
		push	ds			;Yes, remove entry from	list
		push	es			;  by moving the remainder of
		pop	ds			;  the list over this entry.
		mov	si,es:[di]		;Point SI to the next list
		add	si,di			;  entry.
delent_1:
		cmp	word ptr es:[si],-1	;Check for the end of the list
		je	delent_2
		mov	cx,es:[si]		;Get size of entry
		rep	movsb			;Copy next entry over current
		jmp	short delent_1		;  entry.
delent_2:
		mov	word ptr es:[di],-1	;Set end of list indicator
		pop	ds			;Get back file buffer pointer
		pop	si
		pop	cx
		ret
delete_aliasent	endp

;-----------------------------------------------------------------------------
; LOADALIASFILE	loads a	file containing	a list of alias	commands.
; Entry:  DS:SI	- pointer to the name of the file to open
; exit:	     CX	- size of the file in bytes
;	     CF	- clear	if successful
;-----------------------------------------------------------------------------
loadaliasfile	proc	near
		assume	ds:nothing,es:nothing
		xor	bl,bl
		call	scanline		;Find 1st char of filename.
		mov	dx,si			;Copy filename pointer
		inc	bl			;Find end of filename
		call	scanline
		mov	byte ptr [si-1],0	;Make filename ASCIIZ.
		dec	dx
		mov	ax,3d00h		;Open file (Read only)
		int	21h
		jc	loadfile_error
		mov	bx,ax			;Copy file handle
;
;Save the name of the file for error messages.
;
		push	si
		push	es
		mov	di,offset filenam_field
		push	cs
		pop	es
		mov	si,dx
loadfile_1:
		lodsb
		stosb
		or	al,al
		jne	loadfile_1
		mov	byte ptr es:[di-1],"$"	;Terminate string with $.
		pop	es
		pop	si
;
;Open file and read contents into file buffer.
;
		mov	ah,3fh			;Read alias file
		xor	dx,dx			;Point to base of file buffer.
		mov	cx,cs:[filebuf_size]	;Get size of file buffer.
		shl	cx,1			;Convert to bytes
		shl	cx,1
		shl	cx,1
		shl	cx,1
		int	21h
		mov	si,ax
		mov	byte ptr ds:[si],13	;Append	a CR to	end of file.
		mov	cx,ax			;Save new file size
		xor	si,si			;Reset file pointer.
		mov	ah,3eh			;Close file.
		int	21h
		mov	cs:[file_linecount],1	;Reset line counter.
loadfile_exit:
		clc
loadfile_exit1:
		ret
loadfile_error:
		stc
		mov	dx,offset errmsg6	;Bad filename specified.
		jmp	short loadfile_exit1
loadaliasfile	endp

;-----------------------------------------------------------------------------
; LISTALIAS prints the alias list to screen.
; Entry:  ES - segment of the installed	code
;-----------------------------------------------------------------------------
listalias	proc	near
		assume	ds:nothing,es:nothing
		push	ds
;
;Print Command stack size and amount of	alias buffer space remaining
;
		mov	dx,offset infomsg2	;Print size of command stack
		call	printmsg
		mov	ax,es:[cmdstack_size]	;Get size of command stack
		call	hex2asc			;Print size
		mov	dx,offset infomsg3	;Print min cmd length
		call	printmsg
		xor	ax,ax
		mov	al,es:[minlength]	;Get min cmd length to stack
		call	hex2asc			;Print length
		mov	dx,offset infomsg4	;Print label to	buffer size
		call	printmsg
		lds	si,es:[aliaslist_ptr]	;Get pointer to	alias list
listalias_1:
		cmp	word ptr [si],-1	;Scan through alias list to
		je	listalias_2		;  find	the end	of the list.
		add	si,[si]			;Point to next entry
		jmp	short listalias_1
listalias_2:
		mov	ax,es:[aliaslist_size]	;Get offset to end of buffer
		sub	ax,si			;Subtract end of alias list
		sub	ax,2			;Correct for end pointer
		call	hex2asc			;Print size
		mov	dx,offset infomsg5	;Indicate if alias translation
		call	printmsg		;  is enabled or disabled
		mov	dx,offset infomsg5d	;Disabled message
		cmp	byte ptr es:[chk_alias],0	;See if	alias enabled
		je	listalias_3
		mov	dx,offset infomsg5e	;Enabled message
listalias_3:
		call	printmsg
;
;Scan through alias list to print function key assignments
;
		xor	dx,dx			;Clear alias found flag
		lds	si,es:[aliaslist_ptr]	;Get pointer to	alias list
listalias_4:
		cmp	word ptr [si],-1	;Check for end of list
		jne	listalias_5
		jmp	listalias_11
listalias_5:
		mov	bx,si			;Save pointer to entry.
		cmp	byte ptr [si+5],0	;See if	key assignment
		je	listalias_6		;If so,	skip entry
		or	dh,1			;Set alias found flag
		add	si,[bx]			;Point to next entry
		jmp	short listalias_4
listalias_6:
;
;Convert scan code into	function key label
;
		test	dh,80h			;See if	first time though
		jne	listalias_7
		push	dx
		mov	dx,offset infomsg6	;If first time print header
		call	printmsgcr
		pop	dx
		or	dh,80h			;Set header printed flag
listalias_7:
		call	printtab
		push	dx
		add	si,4			;Point SI to command string
		mov	al,[si]			;Get key code
		mov	dh,al
		cmp	al,84			;Check for base	code
		jb	listalias_9
		sub	dh,25
		mov	di,offset shiftmsg	;Assume	shift prefix
		cmp	al,94			;Check for shift
		jb	listalias_8
		sub	dh,10
		mov	di,offset ctlmsg	;Assume	control	prefix
		cmp	al,104			;Check for ctl
		jb	listalias_8
		mov	di,offset altmsg	;Assume	alt prefix
		sub	dh,10
listalias_8:
		push	dx			;Print prefix identifier
		mov	dx,di
		call	printmsg
		pop	dx
listalias_9:
		mov	dl,"F"			;Set function key
		mov	ah,2			;Character output
		int	21h
		mov	al,dh			;Get modified key code
		sub	al,58			;Convert from scan code	to hex
		xor	ah,ah
		call	hex2asc			;Print function	key number
		mov	cl,[bx+3]		;Get length of command
		inc	si			;Point SI to command string
		inc	si
		mov	di,bx
		add	di,[bx]
		cmp	byte ptr [di-1],13	;Check for carrage return
		jne	listalias_10		;  after command. If found,
		mov	dl,'*'			;  indicate immediate command
		mov	ah,2			;  by appending	an * to	the
		int	21h			;  end of the function key.
listalias_10:
		call	printtab
		call	printtab
		call	print_string		;Print command to screen
		mov	dx,offset endmsg	;Append	carriage return	to
		call	printmsg		;  advance to next line. SI
		pop	dx			;  points to the next entry.
		jmp	listalias_4
;
;Scan through alias list to print aliases.
;
listalias_11:
		test	dh,1			;See if	any aliases found
		je	listalias_exit		;If no aliases,	skip remainder
		mov	dx,offset infomsg7	;Print header
		call	printmsgcr
		xor	cx,cx			;Clear CX
		lds	si,es:[aliaslist_ptr]	;Get pointer to	alias list
listalias_12:
		mov	bx,si			;Save pointer to entry.
		cmp	byte ptr [si+5],0	;See if	key definition
		jne	listalias_13		;If so,	skip entry
		add	si,[bx]
		jmp	short listalias_14
listalias_13:
		call	printtab
		add	si,4			;Point SI to alias string
		mov	cl,[bx+2]		;Get length of alias
		call	print_string		;Print alias to	screen
		call	printtab
		call	printtab
		mov	cl,[bx+3]		;Get length of command
		call	print_string		;Print command to screen
		mov	dx,offset endmsg	;Append	carriage return	to
		call	printmsg		;  advance to next line.
listalias_14:
		cmp	word ptr [si],-1	;Check for end of list.	SI
		jne	listalias_12		;  points to the next entry.
listalias_exit:
		mov	dx,offset infomsg8	;Print pointer to help msg
		call	printmsgcr
		pop	ds
		clc
		ret
listalias	endp

;-----------------------------------------------------------------------------
; PRINTTAB prints a TAB	character to the screen.
; Entry:  ES - segment of the installed	code
;-----------------------------------------------------------------------------
printtab	proc	near
		assume	ds:nothing,es:nothing
		mov	ah,2			;Character output
		mov	dl,9			;Print TAB character
		int	21h
		ret
printtab	endp

;-----------------------------------------------------------------------------
; ENABLEALIAS enables alias translation.
; Entry:  ES - segment of the installed	code
;-----------------------------------------------------------------------------
enablealias	proc	near
		assume	ds:nothing,es:nothing
		mov	byte ptr es:[chk_alias],1	;Enable	alias
		clc
		ret
enablealias	endp

;-----------------------------------------------------------------------------
; DISABLEALIAS disables	alias translation.
; Entry:  ES - segment of the installed	code
;-----------------------------------------------------------------------------
disablealias	proc	near
		assume	ds:nothing,es:nothing
		mov	byte ptr es:[chk_alias],0	;Disable alias
		clc
		ret
disablealias	endp

;-----------------------------------------------------------------------------
; REMOVE uninstalls the	installed program from memory.
;-----------------------------------------------------------------------------
remove		proc	near
		assume	ds:nothing,es:nothing
		push	ds
		mov	dx,offset errmsg1	;Not installed msg
		cmp	cs:[alrdy_installed],0	;See if	installed
		je	remove_exit		;Not installed,	error

		mov	cx,cs:[other_seg]	;Get segment of	installed code
		mov	ax,3521h		;Get DOS vector
		int	21h
		mov	ax,es			;Check to make sure DOS
		cmp	ax,cs:[other_seg]	;  vector not modified.
		jne	remove_error

		lds	dx,es:[int2fh]		;Get old interrupt 2F vector
		cmp	dx,-1			;See if	Int used
		je	remove_1		;No, skip check	of Int 2F
		mov	ax,352fh		;Get MUX vector
		int	21h
		mov	ax,es			;Check to make sure MUX
		cmp	ax,cs:[other_seg]	;  vector not modified.
		jne	remove_error
		mov	ax,252fh		;Set interrupt
		int	21h
remove_1:
		lds	dx,es:[int21h]		;Get old interrupt 21 vector
		mov	ax,2521h		;Set interrupt
		int	21h
		mov	cx,es:[env_segment]
		mov	ah,49h			;Free memory block
		int	21h
		mov	es,cx			;Free environment block
		mov	ah,49h
		int	21h
		mov	dx,offset infomsg1	;Indicate uninstalled.
		mov	byte ptr filenam_field,0 ;Clear	filename field
remove_exit:
		stc
		pop	ds
remove_exit1:	ret
remove_error:
		mov	dx,offset errmsg3	;Can't remove error msg
		jmp	short remove_exit

remove		endp

;-----------------------------------------------------------------------------
; COMMENT_LINE allows comments in the alias file by skipping to	the next
; carriage return.
; Entry:  SI - pointer to ASCII	string
;	  CX - file length
;-----------------------------------------------------------------------------
comment_line	proc near
		assume	ds:nothing,es:nothing
comment_loop:
		push	es
		push	ds
		pop	es
		mov	di,si			;Copy file pointer
		mov	al,13			;Scan for carriage return.
		repne	scasb
		inc	cs:[file_linecount]	;Inc file line counter.
		mov	si,di			;Restore file pointer.
		pop	es
		clc
		ret
comment_line	endp

;-----------------------------------------------------------------------------
; HEX2ASC converts a binary number to ASCII and	prints it to the screen.
; Entry:  AX - binary number
;-----------------------------------------------------------------------------
hex2asc		proc near
		assume	ds:nothing,es:nothing
		push	bx
		mov	cx,5			;Allow max of five digits
hex_loop1:
		xor	dx,dx			;Clear high word
		mov	bx,10			;Load number base
		div	bx			;Divide	by base	(10)
		add	dl,30h			;Convert to ascii
		push	dx			;Save digit on stack
		loop	hex_loop1
		mov	cx,5			;Allow max of five digits
		mov	bl,"0"			;Set leading zero indicator
hex_loop2:
		pop	dx			;Get digit off stack
		or	bl,dl			;Don't print leading zeros.
		cmp	bl,"0"			;The first non zero will
		je	hex_1			;  change bl to	non-zero.
		mov	ah,2			;DOS character output
		int	21h
hex_1:
		loop	hex_loop2
hex_exit:
		pop	bx
		ret
hex2asc		endp

;-----------------------------------------------------------------------------
; ASC2BIN - converts an	ASCII number of	the command line to hex.
; Entry:  DS:SI	- pointer to ASCII number
;-----------------------------------------------------------------------------
asc2bin		proc	near
		push	bx
		xor	bl,bl
		call	scanline		;Find next character.
		mov	di,offset errmsg5	;Bad number message
		jc	asc_error		;If no number found, error
		mov	bl,al			;Copy first digit.
		xor	ax,ax			;Clear out sum
		xor	bh,bh			;Clear high byte for word adds
asc_loop:
		cmp	bl," "			;If space, assume end of
		jbe	asc_exit		;  number.
		cmp	bl,"]"			;Exit if closing bracket
		je	asc_exit		;  encountered
		sub	bl,"0"			;Check for valid number	then
		jb	asc_error		;  convert to binary.
		cmp	bl,9
		ja	asc_error
		mov	dx,10			;DX holds base multiplier
		mul	dx			;Shift over current number
		jc	asc_overflow		;If overflow, indicate error
		add	ax,bx			;Add new digit to sum.
		jcxz	asc_exit		;If end	of file, exit.
		mov	bl,ds:[si]		;Get next ASCII	character
		inc	si			;Point to next character
		dec	cx			;Dec file size counter
		jmp	short asc_loop		;Go back for more
asc_exit:
		clc				;Clear error flag.
asc_exit1:
		pop	bx
		ret
asc_overflow:
		mov	di,offset errmsg12	;Number	too large message.
asc_error:
		mov	dx,di			;Copy message pointer.
		stc				;Set error flag.
		jmp	short asc_exit1
asc2bin		endp
;-----------------------------------------------------------------------------
; SCANLINE performs the	same function as SCAN4CHAR but keeps track of the
; carriage returns.
; Entry:  SI - pointer to ASCII	string
;	  BL - 0 = find	next char, 1 = find next space
;	  CX - file length
; Exit:	  AL - first nonspace character
;	  CF - set if carriage return found
;-----------------------------------------------------------------------------
scanline	proc	near
		call	scan4char		;Find the next char.
		jnc	scanline_exit
		inc	cs:[file_linecount]	;Point to next line.
		stc
scanline_exit:
		ret
scanline	endp

end_of_code	=	$
code		ends

end		main
------------
