;    File              : $CMDLINE.A86$
;
;    Description       :
;
;    Original Author   : DIGITAL RESEARCH
;
;    Last Edited By    : $CALDERA$
;
;-----------------------------------------------------------------------;
;    Copyright Work of Caldera, Inc. All Rights Reserved.
;      
;    THIS WORK IS A COPYRIGHT WORK AND CONTAINS CONFIDENTIAL,
;    PROPRIETARY AND TRADE SECRET INFORMATION OF CALDERA, INC.
;    ACCESS TO THIS WORK IS RESTRICTED TO (I) CALDERA, INC. EMPLOYEES
;    WHO HAVE A NEED TO KNOW TO PERFORM TASKS WITHIN THE SCOPE OF
;    THEIR ASSIGNMENTS AND (II) ENTITIES OTHER THAN CALDERA, INC. WHO
;    HAVE ACCEPTED THE CALDERA OPENDOS SOURCE LICENSE OR OTHER CALDERA LICENSE
;    AGREEMENTS. EXCEPT UNDER THE EXPRESS TERMS OF THE CALDERA LICENSE
;    AGREEMENT NO PART OF THIS WORK MAY BE USED, PRACTICED, PERFORMED,
;    COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED, ABRIDGED,
;    CONDENSED, EXPANDED, COLLECTED, COMPILED, LINKED, RECAST,
;    TRANSFORMED OR ADAPTED WITHOUT THE PRIOR WRITTEN CONSENT OF
;    CALDERA, INC. ANY USE OR EXPLOITATION OF THIS WORK WITHOUT
;    AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO CRIMINAL AND
;    CIVIL LIABILITY.
;-----------------------------------------------------------------------;
;
;    *** Current Edit History ***
;    *** End of Current Edit History ***
;
;    $Log$
;    CMDLINE.A86 1.8 93/03/25 15:06:03
;    tweak console block output
;    ENDLOG
;
; 	DOSPLUS Command Line Editor Routines
;

	include	pcmode.equ
	include	i:msdos.equ
	include i:char.def
	include	i:cmdline.equ
	include	i:reqhdr.equ
	include	i:driver.equ


PCM_CODE	CSEG	BYTE
	public	read_line, edit_size

	extrn	dbcs_lead:near
	extrn	cmdline_read:near
	extrn	raw_read:near
	extrn	cooked_write:near
	extrn	get_dseg:near
	extrn	device_driver:near

; WARNING - the following routines are to support history buffers
; As these are optional we muset NEVER call these routines unless
; the HISTORY_ON bit is set in @hist_flag.

	extrn	init_history:near
	extrn	save_history:near
	extrn	prev_cmd:near
	extrn	next_cmd:near
	extrn	match_cmd:near
	extrn	search_cmd:near
	extrn	match_word:near
	extrn	del_cur_history_buffer:near
	extrn	del_history_buffers:near
	extrn	goto_eol:near
	extrn	next_word:near
	extrn	prev_word:near
	extrn	del_bol:near
	extrn	deln_word:near

; The following are public for HISTORY.PCM
	public	next_char, save_line
	public	space_out, bs_out, put_string
	public	goto_bol
	public	del_eol, del_line
	public	char_info
	public	prev_w20
	public	deln_w10


;	READ_LINE will read an editted line from the handle passed in BX
;	into a buffer with the following format:-
;
;	BYTE	Maximum String Length
;	BYTE	Current String Length
;	BYTE(s)	String Buffer
;
;	On Entry:-
;		BX		Input Handle
;		CX		Output Handle
;		ES:DX		Buffer Address
;
;	On Exit:-
;		String input by user
;
;	The following conventions apply for the READ_LINE function
;
;	ES	Buffer segment
;	SI	Current cursor location in buffer (Index)
;	DX	Last Character in Buffer (Index)
;
DISABLE		equ	80h	; Disable when advanced editing is off.
DISABLE_MASK	equ	8000h
ESC_CODE	equ	01h	; Scan code must be preceeded by escape byte.
NESC_CODE	equ	00h	; No lead zero needed.

read_line:
	push	bp			; Save the Stack Frame Pointer
	mov	bp,sp			; Intialise it to the top of the
	sub	sp,RL_LENGTH		; READ_LINE control block and reserve
					; control block
	mov	RL_INPUT,bx		; Initialize the INPUT Handle
	mov	RL_OUTPUT,cx		; the OUTPUT Handle
	inc dx ! inc dx			; Skip max and Returned Length
	mov	RL_BUFOFF,dx		; and save the buffer offset
	mov	RL_BUFSEG,es		;  and segment
	xor	ax,ax			; now we zero
	mov	RL_SAVPOS,ax		;  both position in it
	mov	RL_SAVMAX,ax		;  and it's size
	mov	al,column
	mov	RL_INICOL,ax		; save initial column
	mov	ax,cle_state		; use to set initial editing state
	and	ax,not (RLF_MATCH+RLF_DIRTY+RLF_RECALLED)
	mov	RL_FLAGS,ax		; save in flags
	test	ax,RLF_ENHANCED
	 jz	read_line10
	call	init_history		;  setup the history buffers
	jmps	read_line20
read_line10:
	and	RL_FLAGS,not RLF_INS	; clear insert mode
read_line20:
	mov	di,dx			; di -> buffer
	xor	bx,bx
	or	bl,es:byte ptr -2[di]	; Get the Maximum number of chars
	mov	RL_MAXLEN,bx		; and save for later
	 jnz	read_line30		; make sure some chars are requested
	jmps	ret_string10		; if no chars just return
	
ret_string:
	pop	ax			; Remove local return address
	mov	ax,RL_FLAGS		; get command line editor state
	mov	cle_state,ax		;  save state for next time
	mov	di,RL_BUFOFF		; Get the buffer Offset
	mov	es:byte ptr -1[di],dl	; Return the number of characters
	push	dx			; Save length of entry
	add	di,dx			; Point to the end of the buffer
	mov	al,CR
	stosb				; Save CR
	call	write_char		; Print a CR and return to the user
	pop	dx
	test	RL_FLAGS,RLF_ENHANCED	; Do not add to history if in
	 jz	ret_string10		; compatibility mode
	call	save_history		; Save state of history buffer
ret_string10:
	mov	sp,bp			; Remove READ_LINE control Block
	pop	bp			; Restore BP and return to the caller
	ret


read_line30:
	xor	si,si			; Currently at start of buffer
	mov	dx,si			; with an empty buffer.

	xor	bx,bx
	or	bl,es:byte ptr -1[di]	; Check if the buffer contains any
	 jz	read_line40		; data which is terminated by a CR
	cmp	es:byte ptr [bx+di],CR
	 jnz	read_line40
	mov	dx,bx
read_line40:
	call	save_line		; Update Save Buffer variables
	mov	dx,si
;
; This is out main command loop - we get a character and try to match it
; with a command in our edit_table. If History is on we look at commands
; with the DISABLED bit set ie. enhanced commands.
; It a match isn't found we insert the character in the buffer, and optionally
; try to match with previous lines in the history buffer.
;
read_line_loop:
	and	RL_FLAGS,not RLF_KANJI	; initial flags
	call	get_char		; read the first character (AH Esc Flg)

	mov	cx,edit_size		; now scan the control table looking
	mov	bx,offset edit_table	;  for a match
read_ll_next_cmd:
	and	ax,not DISABLE_MASK	; assume normal function
	test 	RL_FLAGS,RLF_ENHANCED	; compatibilty required? then it
	 jz 	read_ll10		;  has to be a normal function
	test	EDIT_CNTRL,DISABLE_MASK	; history enabled, so we make
	 jz	read_ll10		;  our code match DISABLE mask
	or	ax,DISABLE_MASK		;  of table entry
read_ll10:
	cmp	ax,EDIT_CNTRL		; check for a match (Escape Flag
	 je	read_ll_found_cmd	;  and the character)
	add	bx,EDIT_LEN		; Add the entry length
	loop	read_ll_next_cmd	; and scan the whole table

; We have failed to find a command so insert char in buffer

	test	ah,ESC_CODE		; Ignore non-matched escaped keys
	 jnz	read_line_loop

	call	save_char		; not an command so save the character
	or	RL_FLAGS,RLF_DIRTY	;  and remember we have something new

; Are we in search mode ?

	test 	RL_FLAGS,RLF_ENHANCED	; Compatibilty required?  
	 jz 	read_line_loop
	test	RL_FLAGS,RLF_SEARCH+RLF_MATCH
	 jz	read_line_loop		; is searching/matching on ?
	push	si			; save current offset
	call	search_cmd
	pop	ax			; this is our target offset
read_ll20:
	cmp	ax,si			; are we there yet ?
	 jae	read_line_loop
	push	ax			; no, keep rewinding cursor
	call	prev_char		;  until we reach position
	pop	ax			;  before we tried to match
	jmps	read_ll20

read_ll_found_cmd:			; get the address of the corresponding
	mov	cx,EDIT_FUNC		;  function from the table	
	call	cx			;  execute the correct function
	jmps	read_line_loop		;  and go back for next character

eject
;
;	the SAVE_CHAR routine will write the character in AL into 
;	the buffer in memory and then update the screen image. The
;	RLF_INS flag is used to determine if INSERT is active.
;
save_c10:
	ret

save_char:
	cmp	ah,TRUE			; Ignore any un-matched escape 
	jz	save_c10		; sequences
	call	save_kanji		; Test if AL is a Kanji Character
					; and setup up the parameter blocks
					; for the INTSAVE_CHAR routine
;
;	INTSAVE_CHAR is the internal entry point to the Character Save
;	routine. It assumes the following:-
;
;	On Entry:-	AX(AL)	    Contains the character
;			CX	    the new character length in bytes
;			RLF_KANJI   Flag is set for a Kanji Character
;			RL_KANJI    Contains the kanji charater
;			
intsave_char:
	mov	bx,cx
	test	RL_FLAGS,RLF_INS	; Overwrite the character in the
	 jnz	save_c50		; buffer currently
	add	bx,si			; Add the current index to the character
	cmp	bx,RL_MAXLEN		; size and compare against the buffer len
	 jae	bell_char		; Full ? Yes Ring dat Bell !

	cmp	dx,si			; Are we at the end of the line
	 jnz	intsave_c10		; No so check character types
	push ax ! push cx
	call	skip_one_char		; Skip the coresponding character in
	pop cx ! pop ax			; the save buffer
	jmps	simple_save

intsave_c10:
	push	ax			; Save the Input Character
	call	char_type		; Get the character type
	mov	bx,ax			; and save in BX
	mov	al,es:[di]		; get the byte to be replaced
	call	char_type		; and get its type
	and	ah,CHAR_SIZE		; Mask the Character SIZE attributes
	and	bh,CHAR_SIZE		; and check both storage and display
					; sizes are the same for old and new
	cmp	ah,bh			; and do simple save if the character
	pop	ax			; Restore the input character to AX(AL)
	 jnz	save_c30		; type match
	sub	dx,cx			; Character overwritten so prevent
					; Max Index being incremented

simple_save:
	add	si,cx			; Assume at the EOL
	add	dx,cx
	stosb				; Save the character typed
	test	RL_FLAGS,RLF_KANJI	; is this a Kanji character
	 jz	simple_s10		; No so just output 1 character
	call	put_char		; and echo it to the user
	mov	al,byte ptr RL_KANJI+1	; Get the high byte of the Kanji
	stosb				; character save and then display it.
simple_s10:
	jmp	put_char
;	ret

;
;	The SAVE_C30 function supports the Complex overwrite conditions
;	where the size of the character in memory or on the display do not
;	match with those of the present incumbent. eg a SPACE character
;	overwriting a TAB or a KANJI character overwriting a SPACE.
;
;	To minimize the complexity of the code the character to be 
;	overwritten is deleted and the new character then inserted.
;	This is not an optimal solution but drastically reduces the
;	amount of code required.
;
save_c30:
	push ax ! push cx
	call	deln_char
	pop cx ! pop ax
	cmp	dx,si
	 jz	simple_save
	or	RL_FLAGS,RLF_INS
	call	save_c50
	and	RL_FLAGS,not RLF_INS
	ret

bell_char:
	mov	al,BELL
	jmp	write_char
;
;	This code is called when INSERT mode is active and a 
;	character (possibly Kanji) is to be inserted in the buffer
;
;	On Entry:-	CX	    the new character length in bytes
;
save_c50:
	mov	bx,cx			; Save new character length
	add	cx,dx			; Add the current max to the character
	cmp	cx,RL_MAXLEN		; size and compare against the buffer len
	 jae	bell_char		; Full ? Yes Ring dat Bell !
	mov	cx,bx			; Restore Character Length
	cmp	dx,si			; If we are at the end of the line
	 je	simple_save		; Use the simple save code
;
;	Create space in the current buffer for the new character
;
	push ds
	push si ! push di
	mov cx,dx ! sub cx,si		; CX -> Number of bytes to move
	mov di,dx ! add di,RL_BUFOFF	; DI -> End of Destination Offset
	add di,bx ! dec di		;    -> + Insert Char len - 1
	mov si,di ! sub si,bx		; SI -> DI - Insert Char Len
	push es ! pop ds		; DS == ES
	std				; Make the right amount of space in
	rep	movsb			; the buffer	
	cld
	pop di ! pop si
	pop	ds

	add	dx,bx			; Update the Buffer Length
	stosb				; Save the New character
	test	RL_FLAGS,RLF_KANJI	; Check if this was a Kanji Character
	 jz	save_c60		; No
	xchg al,ah ! stosb		; Yes Save high byte

save_c60:
	mov cx,dx ! sub cx,si		; Display the updated string
	add si,bx ! push si		; Save the Updated Index
	mov si,di ! sub si,bx		; Get the offset of the new char
	call	put_string		; in the buffer and display all
	pop	si			; Restore the new index
	xchg	di,dx			; and calculate the number of BS
	call	calc_chars		; characters required to get back
	xchg	di,dx
	jmp	bs_out
;
;	On Entry:	AL	First byte of Character
;
;	On Exit:	AX	Complete Character Code
;			CX	Character Size Bytes
;			RL_KANJI and RLF_KANJI set correctly
;
save_kanji:
	and	RL_FLAGS,not RLF_KANJI
	mov	RL_KANJI,ax		; Save the Character
	call	char_type		; Is this the first byte of a 
	test	ah,CHAR_KANJI		; two byte Kanji character
	mov	cx,1			; Character size in bytes
	 jz	save_k10		; No
	or	RL_FLAGS,RLF_KANJI	; Set internal Flag
	call	get_char		; Get the high byte and save
	mov	byte ptr RL_KANJI+1,al	; in the local variable
	mov	ax,RL_KANJI		; Get the complete character
	mov	cx,2			; Character size in bytes
save_k10:
	ret

eject
;
;	The following group of functions modify the flags which control
;	the command line editor.
;
toggle_ins:
	xor	RL_FLAGS,RLF_INS	; Toggle the OverWrite/Insert
	ret				; Flag

toggle_search:
	and	RL_FLAGS,not RLF_MATCH	; clear match bit
	xor	RL_FLAGS,RLF_SEARCH	; Toggle the Search on/off flag
	ret

eject
;
;	This group of functions moves the cursor along the display
;	as well as updating the local variables.
;
goto_bol:
	test	si,si			; Move the cursor to the begining of
	 jz	goto_b10		; the displayed line
	mov	di,si			; Set the buffer index to the
	xor	si,si			; start of the line and the current 
	call	calc_chars		; location
	call	bs_out
	xor	si,si
	mov	di,RL_BUFOFF
goto_b10:
	ret

next_char:
	cmp	si,dx
	 jnz	next_c05		; Treat this as "F1" when we at the
	jmp	copy_char		; end of the line

next_c05:
	mov	al,es:[di]		; Get the Offset of the next character
	mov	cx,1			; the character itself and assume
	call	char_type		; it is 1 byte long
	test ah,CHAR_KANJI ! jz next_c10
	inc	cx
next_c10:
	xchg	si,di			; Get the string offset in SI
	call	put_string		; display the character and
	xchg	si,di			; restore the register contents
	add	si,cx
	add	di,cx
	ret

prev_char:
	test	si,si			; begining of line ?
	 jz	prev_w30
	push dx ! push si ! push di
	mov	si,RL_BUFOFF		; Scan from the begining of the buffer
	mov	dx,si			; keeping the last match in DX
prev_c10:
	call	char_info		; Get the character information
	cmp	si,di			; Stop when we get to the current
	 je	prev_w20		; character location
	mov	dx,si			; Save current location
	jmps	prev_c10		; and repeat

prev_w20:
	sub	si,dx			; Calculate character length
	push	si			; save for update

	sub	di,RL_BUFOFF		; Convert Offset to Index
	neg si ! add si,di		; Set the buffer index to the current
	call	calc_chars		; location and the previous character
	call	bs_out			; BackSpace over character
	pop	cx			; Restore the character size
	pop di ! pop si ! pop dx
	sub	si,cx			; Update the Index and Pointer
	sub	di,cx			; variables.
prev_w30:
	ret

eject
;
;	This group of functions deletes characters or groups of characters
;	from the buffer.
;

delf_char:              
	cmp	si,dx			; any chars to our right ?
	 jb	deln_char		;  yes, delete them first
    jmp skip_one_char      
;	ret				;  discard next saved char

del_eol:
	mov cx,dx ! sub cx,si		; Calculate the number of bytes to 
	jcxz	del_eol10		; delete and jump to DELN_WORD if
	add	cx,di			; non zero. Convert to an offset
	jmps	deln_w10		; and jmp to common code.	
del_eol10:
	ret

delp_char:
	or si,si ! jz del_eol10		; Ignore if the user is at the start 
	call	back_one_char		; of the line otherwise move back one
	call	prev_char		; character in the line buffer


deln_char:
	cmp dx,si ! jz del_eol10
	mov	al,es:[di]		; Get the Offset of the next character
	lea	cx,1[di]		; the character itself and assume
	call	char_type		; it is 1 byte long
	test ah,CHAR_KANJI ! jz deln_w10
	inc	cx
;	jmps	deln_w10

;
;	The 3 delete functions come together at this point with the standard
;	register format Plus CX is the offset of the first character not to
;	be deleted.
;
deln_w10:
	push	cx			; Save Delete Offset
	xchg	di,dx			; Determine the no of characters
	call	calc_chars		; displayed to the end of the line
	xchg	di,dx
	mov	bx,cx			; Save the Column count
	pop	ax			; restore the delete offset

	push bx ! push bx		; Save the count twice

	push si ! push di
	mov cx,dx ! sub cx,si		; No of chars from old EOL
	mov	si,ax			; Get the Source Offset
	sub	ax,di			; calculate its length.
	sub	dx,ax			; Update the string length

	sub	cx,ax			; Number of chars to copy
	push	ds			; Move the contents of the
	push es ! pop ds		; string down in memory and
	rep	movsb			; then update the screen image
	pop	ds
	pop si ! pop di			; Get the current buffer offset
					; Restore SWAPPED SI <-> DI

	mov	cx,dx			; Calculate the length of the
	sub	cx,di			; string and print it alll
	call	put_string
	xchg	si,di			; Restore SI and DI

	 jcxz	deln_w20
	xchg	di,dx			; Calculate the number of columns
	call	calc_chars		; displayed
	xchg	di,dx

deln_w20:
	pop	bx			; Restore the original line length
	sub	bx,cx			; and calculate the number of spaces
	mov	cx,bx			; required to overwrite the data
	call	space_out

	pop	cx			; Finally move the cursor back to
	jmp	bs_out			; its correct place 




;
;	Delete the contents of the complete line
;
del_line:
	mov	RL_SAVPOS,0		; Reset the buffer index
	test	dx,dx
	 jz	del_l10
	call	goto_bol		; Jump to the begining of the line
	mov	di,dx			; calculate the number of display
	call	calc_chars		; columns it currently takes up
	call	space_out		; Overwrite with spaces
	call	bs_out			; Move back to the start of the line
	xor	si,si			; and update all the initial variables
	mov	dx,si
	mov	di,RL_BUFOFF
del_l10:
	ret

eject
;
;	The following routines manipulate the SAVE Buffer data. Which
;	is initialised on entry to this function.
;
;	SKIP_ONE_CHAR increments the Save Buffer control variables and
;	returns the number of bytes skipped in CX.
;
;	On Entry:	Standard Registers
;
;	On Exit:	AX 	Next Character in Buffer
;			CX	Character Size (Bytes)
;
skip_one_char:
	xor	cx,cx			
	mov	bx,RL_SAVPOS		; Update the Save Buffer variables
	cmp	bx,RL_SAVMAX		; Check the current save buffer is
	 jae	soc_20			; valid and has not been exhausted.
					; Otherwise increment the RL_SAVPOS
	mov	bx,offset savbuf	; pointer by one character. This 
	add	bx,RL_SAVPOS		; means that the RL_SAVPOS can be 
	mov	al,ds:[bx]		; incremented by 1 or 2 depending on 
	call	char_type		; the contents of the buffer
	test	ah,CHAR_KANJI
	 jz	soc_10
	mov	ah,ds:1[bx]
	inc	cx
soc_10:
	inc	cx
soc_20:
	add	RL_SAVPOS,cx
	ret
;
;
;	BACK_ONE_CHAR decrements the Save Buffer control variables and
;	returns the number of bytes skipped in CX.
;
;	On Entry:	Standard Registers
;
;	On Exit:	RL_SAVPOS	points to previous buffer char
;			AX,BX,CX,DX	Unknown
;
back_one_char:
	push	dx
	mov	bx,offset savbuf	; Get the Buffer address
	mov cx,bx ! add cx,RL_SAVPOS	; CX is the Current location
	mov	dx,bx			; DX is last matching character	
boc_10:
	cmp	bx,cx			; Have we reached the current Char
	 jz	boc_20			; Yes exit and update buffer
	mov	dx,bx			; Update last character location
	mov	al,ds:[bx]		; incremented by 1 or 2 depending on 
	call	char_type		; the contents of the buffer
	inc	bx
	test	ah,CHAR_KANJI		; Increment pointer by 2 for a Kanji
	 jz	boc_10			; character
	inc	bx
	jmps	boc_10

boc_20:	
	sub	dx,offset savbuf	; Calculate the character Index
	mov	RL_SAVPOS,dx		; and save in RL_SAVPOS
	pop	dx
	ret

copy_char:
	cmp	dx,si		; If at end of line copy characters 
	jz	copy_c5
	call	next_char	; Otherwise just move by 1
	jmp	copy_c10
copy_c5:
	call	skip_one_char		; Calculate Bytes to copy
	jcxz	copy_c10		; Skip Update in no characters skipped
	sub	RL_SAVPOS,cx		; Restore the Buffer Position
	jmps	copy_a10		; and copy the data
copy_c10:
	ret

copy_till_char:
	cmp	dx,si			; Copy out if at end of line
	jnz	move_till_char
	call	skip_till_char		; Returns index to the next char
	sub	RL_SAVPOS,cx
	jmps	copy_a10
move_till_char:
	mov	RL_SAVPOS,si		; Start search from the current
	call	skip_till_char		; position
	jcxz	no_move			; CX=0 - dont move
move_along:
	push	cx
	call	next_char		; Shuttle along the line until
	pop	cx			; we reach the character
	loop	move_along
no_move:
	ret
	
copy_all:
	mov	cx,RL_SAVMAX		; Calculate the number of bytes to
	sub	cx,RL_SAVPOS		; copy from the buffer.
copy_a10:
	cmp	cx,0			; do we have nothing to copy
	 jle	copy_a30		; (or less than nothing..)

	push	RL_FLAGS		; Save State flags and prevent
	or	RL_FLAGS,RLF_INS	; SAVPOS being modified
copy_a20:
	push	cx
	and	RL_FLAGS,not RLF_KANJI
	call	skip_one_char		; Return the next character and its 
	cmp 	cx,1 
	jz 	copy_a25		; size in bytes
	mov	RL_KANJI,ax		; Save the Kanji Character and
	or	RL_FLAGS,RLF_KANJI	; set the control flag
	pop 	bx
	dec 	bx
	push 	bx			; Decrement the Loop Count
	
copy_a25:
	call	intsave_char		; Save the character
	pop	cx			; and repeat till all bytes have
	loop	copy_a20		; been copied
	pop	RL_FLAGS		; Restore State Flags
copy_a30:
	ret

skip_till_char:
	call	get_char		; Get the first character
	call	save_kanji		; Setup RL_KANJI etc.
	push	dx
	call	skip_one_char		; don't match on 1st char
	mov	dx,cx			; remember we've skipped 1st char
	 jcxz	stc_40			; buffer exhausted
stc_10:
	call	skip_one_char		; Get the Next Character
	jcxz	stc_40			; Buffer exhausted
	add	dx,cx			; Update the Total Byte Count
	cmp cx,2 ! jz stc_20		; Was this a Kanji Character
	test RL_FLAGS,RLF_KANJI		; No but are we looking for one ?
	jnz	stc_10			; Yes so get the next character
	cmp	al,byte ptr RL_KANJI	; Have we got a matching character ?
	jnz	stc_10			; No so look again
	jmps	stc_30			; Return Sucess 

stc_20:					; Kanji Character in Buffer
	test RL_FLAGS,RLF_KANJI		; Are we looking for a Kanji Char
	jz	stc_10			; No so try again
	cmp	ax,RL_KANJI		; Check the character and repeat
	jnz	stc_10			; if they donot match

stc_30:					; Character Match
	sub	dx,cx			; Correct the Total Byte Count
	sub	RL_SAVPOS,cx		; point to the matching char
	xchg	cx,dx			; and return the Match Count
	pop	dx
	ret

stc_40:					; No Match
	sub	RL_SAVPOS,dx		; Restore RL_SAVPOS to orginal value
	xor	cx,cx			; and return 0000 characters skipped
	pop	dx
	ret

;
;	Update the Save buffer with the contents of the users
;	line buffer.
;
;	On Entry:	ES:DI -> Current location in Buffer
;			DX	 No. of bytes in buffer
;
;	On Exit:	Update RL_SAVMAX, RL_SAVPOS, RL_SAVBUF
;
save_line:
	xor	ax,ax
	mov	RL_SAVPOS,ax
	mov	RL_SAVMAX,ax
	mov	cx,dx			; Current Line Length
	 jcxz	save_l10
	cmp	cx,savbuf_size		; clip the amount saved
	 jb	save_l5			; to be a maximum of
	mov	cx,savbuf_size		; the save buffer size
save_l5:
	mov	RL_SAVMAX,cx		; Set the Save Data Length
	push ds ! push es
	push si ! push di

	push ds ! pop es
	lds	si,RL_BUFPTR
	mov	di,offset savbuf
	rep	movsb			; save the data
	pop di ! pop si
	pop es ! pop ds
save_l10:
;	ret

ignore_char:
	ret

mem_line:		;; JFL save from beginning of line 
	call	save_line
	jmp	goto_bol
;	ret	


eof_char:		;; JFL make F6 return a CTL Z
	mov	al,01Ah
	mov	cx,1
	jmp	intsave_char
;	ret

ctlat_char:		;; JFL make F7 return a CTL @
	mov	al,0h
	mov	cx,1
	jmp	intsave_char
;	ret


eject
;
;	CHAR_TYPE get the next character from the buffer ES:SI and returns
;	its type in AH using the equates CHAR_????. The character is returned
;	in AL.
;
;
;	
char_type:
	mov ah,CHAR_SPACE or CHAR_STD	; WhiteSpace
	cmp al,' ' ! jz char_t100
	mov	ah,CHAR_TAB		; Tab Character
	cmp al,TAB ! jz char_t100
	mov ah,CHAR_ALPHAN or CHAR_STD	
	cmp al,CTLU ! jz char_t100	; Control-U and Control-T are treated
	cmp al,CTLT ! jz char_t100	; as normal characters
	mov	ah,CHAR_CTL		; Control Character
	cmp al,' ' ! jb char_t100

	mov ah,CHAR_ALPHAN or CHAR_STD	
	cmp al,'0' ! jb  char_t90	; Return SYMBOL
	cmp al,'9' ! jbe char_t100	; Return AlphaNumeric
	cmp al,'A' ! jb  char_t90	; Return Symbol
	cmp al,'Z' ! jbe char_t100	; Return AlphaNumeric
	cmp al,'a' ! jb  char_t90	; Return Symbol
	cmp al,'z' ! jbe char_t100	; Return AlphaNumeric
	cmp al,80h ! jb  char_t90	; Return Symbol

	mov	ah, CHAR_KANJI		; assume character is 16 bits
	call	dbcs_lead		; is byte a DBCS lead?
	je	char_t100		;  yes - done
char_t90:
	mov	ah,CHAR_OTHER or CHAR_STD ; no - Normal Character Symbol

char_t100:
	ret





;
;	CHAR_INFO will return various information about the character
;	at ES:SI
;
;	On Entry:	ES:SI	Character Pointer
;			BX	Current Column No.
;			CX	Byte Scan Count
;
;	On Exit:	ES:SI	Points to the next Character
;			BX	Updates Column No.
;			CX	Updates Byte Scan Count
;			AH	Character type Flags
;			AL	First byte of Character
char_info:
	lods	es:al
	call	char_type		; Test the character type and assume
	inc	bx			; it will take 1 Screen location
	test	ah,CHAR_ONECOL
	jnz	char_i20

	inc	bx			; Now check for the Control Characters
	test	ah,CHAR_CTL		; which take up 2 cols
	jnz	char_i20

	test	ah,CHAR_KANJI		; If this was the first byte of a 
	jz	char_i10		; KANJI character then skip the
	inc si ! dec cx			; next byte
	jmps	char_i20

char_i10:
	push	ax			; Save AX and calculate the number
	dec bx ! dec bx			; of screen locations that this TAB
	mov	ax,bx			; character will use based on the fact
	and 	ax,7			; BX contains the current column
	neg ax ! add ax,8
	add	bx,ax
	pop	ax

char_i20:
	ret

eject
;
;	CALC_CHARS calculates the number of character locations used
;	on the screen to display a particular sub-string of the current
;	buffer. This routine takes account of the Kanji, Control and TAB
;	characters.
;
;	On Entry:	SI	Start Buffer Index
;			DI	End Buffer Index
;
;	On Exit:	CX	Count
;
calc_chars:
	push	bx
	push si ! push di
	mov	bx,RL_INICOL		; Get the initial Column
	sub	di,si			; DI = Sub-string length bytes
	mov	cx,si			; Use the Start Index for the
	mov	si,RL_BUFOFF		; initial count and scan from the
	jcxz	calc_c20		; start of the buffer

calc_c10:
	call	char_info
	loop	calc_c10

calc_c20:
	mov	cx,di			; Sub-String Length
	mov	di,bx			; Current Column position

calc_c30:
	call	char_info
	loop	calc_c30

	sub	bx,di
	mov	cx,bx
	pop di ! pop si
	pop	bx
	ret

eject
;
;	The following functions are purely Low level character output
;	functions.
;
space_out:				; Write CX Space characters to the
	mov	al,' '			; Output handle
	jmps	block_out
	
bs_out:
	mov	al,CTLH			; Write CX BackSpace characters to
;;	jmps	block_out		; Output Handle

block_out:
	 jcxz	block_o20
	push	cx
block_o10:
	call	write_char		; output this character
	loop	block_o10
	pop	cx
block_o20:
	ret

;
;	Display CX characters from the string at ES:SI
;
put_string:
	jcxz	puts_s20
	push cx ! push si
put_s10:
	push	cx
	lods	es:al
	call	put_char
	pop	cx
	loop	put_s10
	pop si ! pop cx
puts_s20:
	ret

;
;	Display the character in AL
;
put_char:
	cmp al,' ' ! jae write_char	; skip if it's printable
	cmp al,CTLT! je write_char	; Control-T and Control-U are treated
	cmp al,CTLU! je	write_char	; like normal Characters.
	cmp al,TAB ! je write_char	; skip if it's TAB
	or	al,040h			; Convert Character to Uppercase
	push	ax			; save it
	mov	al,'^'			; display the character
	call	write_char		;  in ^X format
	pop	ax
write_char:
	push	es
	push	dx
	push	cx
	push	si
	push	di
	push	ax			; char on stack
	mov	bx,RL_OUTPUT		; Output AL to the OUTPUT Handle
	push ss ! pop es		; ES:DX -> Character Buffer
	mov	si,sp			; buffer offset
	mov	cx,1			; character count
	call	cooked_write		; Write Character
	pop	ax
	pop	di
	pop	si
	pop	cx
	pop	dx
	pop	es
	ret
;
;	Read 1 a character from RL_INPUT saving all the vital registers
;	from corruption. If the first character is the ESCAPE character
;	set AH to TRUE and read the next byte. Otherwise AH is FALSE
;
get_char:
	push	ds
	push	es
	push	dx
	push	si
	push	di

	call	get_dseg		; point at pcmode data
	lds	si,con_device		; DS:SI -> current console device
	test	ds:DH_ATTRIB[si],DA_IOCTL ; test bit 14: IOCTL bit
	 jz	get_c05			; error if IOCTL not supported
	sub	sp,RH_SIZE		; reserve this many words on the stack
	mov	bx,sp			; SS:BX -> request packet
	push ss ! pop es		; ES:BX -> request packet
	lea	dx,RL_FLAGS		; point at flags
	mov	es:RH_CMD[bx],CMD_OUTPUT_IOCTL
	mov	es:RH_LEN[bx],RH4_LEN
	mov	es:RH4_BUFOFF[bx],dx	; set up for a normal
	mov	es:RH4_BUFSEG[bx],ss	;  IOCTL read/write
	mov	es:RH4_COUNT[bx],WORD
	call	device_driver		; execute the command
	add	sp,RH_SIZE		; reclaim the stack
get_c05:

	call	get_dseg		; point at pcmode data
	mov	bx,RL_INPUT		; BX = input stream
	call	cmdline_read		; get a character

	mov	ah,FALSE		; Assume this is NOT the Escape 
	cmp	al,esc_char		; character and set high byte of the
	 jnz	get_c10			; match word to FALSE

	call	raw_read		; read the second byte of the escape
	mov	ah,ESC_CODE		;  sequence, setting high byte to ESC
get_c10:
	pop	di
	pop	si
	pop	dx
	pop	es
	pop	ds
	ret

PCM_RODATA	CSEG	WORD

EDIT_CNTRL	equ	cs:word ptr 0[bx]	; Character and Esc Flag
EDIT_FUNC	equ	cs:word ptr 2[bx]	; Edit Function Address
EDIT_LEN	equ	4			; Edit Table Entry Size

esc_char	db	0		; Command Line Editor Escape Character
edit_size	dw	(offset edit_end - edit_table)/EDIT_LEN

edit_table	db CR	! db NESC_CODE ! dw ret_string
	db LF	! db NESC_CODE ! dw ignore_char
	db CTLH	! db NESC_CODE ! dw delp_char
	db DEL	! db NESC_CODE ! dw delf_char

	db ESC	! db NESC_CODE ! dw del_line
	db ';'  ! db ESC_CODE  ! dw copy_char		; Function 1
	db '<'  ! db ESC_CODE  ! dw copy_till_char	; Function 2
	db '='  ! db ESC_CODE  ! dw copy_all		; Function 3
	db '>'  ! db ESC_CODE  ! dw skip_till_char	; Function 4
	db '?'  ! db ESC_CODE  ! dw mem_line		; Function 5

	db '@'  ! db ESC_CODE  ! dw eof_char		; Function 6
	db 'A'  ! db ESC_CODE  ! dw ctlat_char		; Function 7
	db 'B'	! db ESC_CODE or DISABLE ! dw match_cmd	; Function 8

	db 'R'  ! db ESC_CODE  ! dw toggle_ins		; Insert
	db 'S'  ! db ESC_CODE  ! dw delf_char		; Delete
	db 'K'  ! db ESC_CODE or DISABLE  ! dw prev_char ; Left Arrow 
	db 'M'  ! db ESC_CODE or DISABLE  ! dw next_char	; Right Arrow
; When advanced editing is disabled the match for Left/Right arrows will fall
; through to here
	db 'K'  ! db ESC_CODE  ! dw delp_char		; Left Arrow - compat
	db 'M'  ! db ESC_CODE  ! dw copy_char		; Right Arrow - compat
;
; Extended functions from here on
;
	db 'C'  ! db ESC_CODE or DISABLE ! dw del_cur_history_buffer ; Func 9
	db 'D'  ! db ESC_CODE or DISABLE ! dw del_history_buffers ; Func 10
	db 'G'  ! db ESC_CODE or DISABLE  ! dw goto_bol		; Home
	db 'O'  ! db ESC_CODE or DISABLE  ! dw goto_eol		; End
	db 't'  ! db ESC_CODE or DISABLE  ! dw next_word	; Control Right Arrow
	db 's'  ! db ESC_CODE or DISABLE  ! dw prev_word	; Control Left Arrow


	db CTLV	! db NESC_CODE or DISABLE ! dw toggle_ins
	db CTLQ	! db NESC_CODE or DISABLE ! dw goto_bol
	db CTLW	! db NESC_CODE or DISABLE ! dw goto_eol
	db CTLR	! db NESC_CODE or DISABLE ! dw match_cmd
	db CTLD	! db NESC_CODE or DISABLE ! dw next_char
	db CTLS	! db NESC_CODE or DISABLE ! dw prev_char
	db CTLF	! db NESC_CODE or DISABLE ! dw next_word
	db CTLA	! db NESC_CODE or DISABLE ! dw prev_word
	db CTLG ! db NESC_CODE or DISABLE ! dw deln_char
	db CTLT ! db NESC_CODE or DISABLE ! dw deln_word
	db CTLY ! db NESC_CODE or DISABLE ! dw del_line
	db CTLB ! db NESC_CODE or DISABLE ! dw del_bol
	db CTLK ! db NESC_CODE or DISABLE ! dw del_eol

	db 'I'  ! db ESC_CODE or DISABLE  ! dw ignore_char	; PageUP
	db 'Q'  ! db ESC_CODE or DISABLE  ! dw ignore_char	; PageDown
	db 'H'  ! db ESC_CODE or DISABLE  ! dw prev_cmd 	; Up Arrow
	db 'P'  ! db ESC_CODE or DISABLE  ! dw next_cmd		; Down Arrow
	db CTLE ! db NESC_CODE or DISABLE ! dw prev_cmd
	db CTLX ! db NESC_CODE or DISABLE ! dw next_cmd
	db CTLUB ! db NESC_CODE or DISABLE ! dw toggle_search	; Default search mode
edit_end	rb	0


PCMODE_DATA	DSEG	WORD

	extrn	con_device:dword	; Current Console Device
	extrn	column:byte 		; Console Cursor Location

	savbuf_size	equ	128

	extrn	savbuf:byte		; fixed location in DOS data area

	extrn	cle_state:word		; command line editing state

	end

