Metropoli BBS
VIEWER: edit.asm MODE: TEXT (ASCII)
; EDIT.ASM
; (c) 1989, 1990 Ashok P. Nadkarni
;
; This module contains the line editing functions of CMDEDIT.

	PUBLIC	get_kbd_line
	PUBLIC	auto_recall
	PUBLIC	expand_fnkey

	INCLUDE common.inc
	INCLUDE ascii.inc
	INCLUDE	BIOS.INC
	INCLUDE	DOS.INC
	INCLUDE	GENERAL.INC


CSEG	SEGMENT	PARA PUBLIC 'CODE'
CSEG	ENDS

DGROUP	GROUP	CSEG

	ASSUME	CS:DGROUP, DS:DGROUP, SS:DGROUP, ES:DGROUP

CSEG	SEGMENT	PARA PUBLIC 'CODE'

	EXTRN	linebuf:BYTE
	EXTRN	lastchar:WORD
	EXTRN	caller_cursor:WORD
	EXTRN	dot:WORD
	EXTRN	edit_mode:BYTE
	EXTRN	omode_cursor:WORD
	EXTRN	sym_stk:WORD
	EXTRN	LINEBUF_END:ABS


tempchar db	?			;Temporary storage
auto_recall	db	0		;By default no auto recall
auto_recall_on	db	?		;1 if auto-recall in effect,
;					 else 0
continue_recall db	?		;auto-recall state variable
fnkey_tab	db	'S','F',0	;Used to look for function key
;					 definitions. 
fnkey_exec_char db	'@'		;If the last character a fn key
;					 definition is this, the key is
;					 executed immediately

; Must be IDENTICAL to the string in CMDCONF.C including the
; terminating '\0' byte. This must be followed immediately by ctrl_key_defs.
cmdedit_keymap_id db	'CMDEDIT key map',0
; ctrl_key_redefs is used to allow the cmdconf program to remap the control
; keys. Each entry corresponds to a control key. It must immediately follow
; cmdedit_keymap_id.
ctrl_key_redefs LABEL WORD
x	=	0
	REPT	32				;32 control keys
	DW	x
x	=	x + 1
	ENDM


	EXTRN	disp_line:PROC
	EXTRN	hist_back:PROC
	EXTRN	hist_fwd:PROC
	EXTRN	hist_bck_match:PROC
	EXTRN	hist_fwd_match:PROC
	EXTRN	remember:PROC
	EXTRN	execute_auto_recall:PROC
	EXTRN	insert_at_dot:PROC
	EXTRN	erase_to_dot:PROC
	EXTRN	isalphnum:PROC
	EXTRN	set_disp_marks:PROC
	EXTRN	bell:PROC
	EXTRN	line_to_scr:PROC
	EXTRN	isspace:PROC
	EXTRN	xlate_lower:PROC
	EXTRN	expand_var:PROC
	EXTRN	ismacsym:PROC
	EXTRN	get_symbol:PROC
	
;number of command keys
NUM_CMD_KEYS	EQU	(cmd_key_jumps-cmd_key_table)/2

;+
; FUNCTION : get_kbd_line
;
;	Gets a line from the user and stores it in linebuf. The length
;	of the line is stored in global linelen. The name is a misnomer
;	because the line is from standard input, not necessarily from
;	the keyboard.
;
; Parameters:
;	None.
;
; Returns:
;	Nothing.
; Register(s) destroyed:
;-

get_kbd_line	proc	near
	@save	si,di
	mov	al,auto_recall
	mov	auto_recall_on,al	;Indicate if auto-recall is enabled
	mov	continue_recall,1	;Init auto-recall memory

; Main editing loop
@get_kbd_line_10:
	mov	al,continue_recall
	and	auto_recall_on,al	;Indicate if we should keep
;					 auto-recalling
	mov	continue_recall,0	;Reset the flag. It will be set
;					 for the default actions keys.
	call	near ptr disp_line	;Show current line
	call	near ptr getkey		;get next key from the user
	mov	si,ax			;si holds the character
	cmp	ax,32			;Control character ?
	jge	@get_kbd_line_15	;No, then check if fn key
	mov	di,si			;DI used to calculate
	add	di,di			;  offset into table
	mov	ax,ctrl_key_redefs[di]	;Mapping of key
	mov	si,ax
	cmp	ax,32			;Control char ?
	jge	@get_kbd_line_15	;No, then check if fn key
	mov	di,si			;DI used to calculate
	add	di,di			;  offset into table
	jmp	ctrl_key_table[di]	;Branch according to control char
@get_kbd_line_15:			;Check if function key
	cmp	ax,256+59		;>= F1 ?
	jb	@get_kbd_line_20	;No, then check next table
	cmp	ax,256+68		;<= F10
	jg	@get_kbd_line_20	;Not fn key
	jmp	@fn_key			;Handle the function key
@get_kbd_line_20:
	cmp	ax,256+84		;>= Shift-F1 ?
	jb	@get_kbd_line_25	;No, then check next table
	cmp	ax,256+93		;<= Shift-F10
	jg	@get_kbd_line_25	;
	jmp	@sfn_key		;Handle the shifted function key
@get_kbd_line_25:
	mov	cx,NUM_CMD_KEYS		;size and...
	mov	bx,offset DGROUP:cmd_key_table	;beginning of jump table
@get_kbd_line_30:			;Loop begin
	cmp	ax,cs:[bx]		;Is this the key ?
	je	@get_kbd_line_40	;Hurrah, found
	inc	bx			;point to next key
	inc	bx
	loop	@get_kbd_line_30	;If more keys, repeat
	jmp	short @default_action	;key not in table
@get_kbd_line_40:			;Jump to location associated with
	jmp	word ptr cs:[bx+(2*NUM_CMD_KEYS)]	;key

@quote:					;Insert next char
;					 without interpreting it
	call	near ptr  getkey

@default_action:			;Insert the character (in AX)
	call	near ptr store_char
	jc	@get_kbd_line_10	;No room for char, keep looping
	mov	al,auto_recall_on	;Are we auto-recalling ?
	or	al,al
	jz	@get_kbd_line_10	;No
	call	near ptr execute_auto_recall
	jc	@get_kbd_line_10	;If carry set, no matching
;					 string in history buffer
	mov	continue_recall,1	;Else indicate auto-recall is
;					 still to go on
	jmp	short @get_kbd_line_10


@char_left:				;Cursor one char left
	call	near ptr char_left	;char_left will return to top
	jmp	@get_kbd_line_10
;					 of editing loop

@char_right:				;Cursor one char right
	call	near ptr char_right	;char_right will return to top
	jmp	@get_kbd_line_10
;					 of editing loop

@word_left:				;Cursor one word left
	call	near ptr word_left
	jmp	@get_kbd_line_10

@word_right:				;Cursor one word right
	call	near ptr word_right
	jmp	@get_kbd_line_10

@bol:					;Beginning of line
	call	near ptr go_bol
	jmp	@get_kbd_line_10


@eol:					;End of line
	call	near ptr go_eol
	jmp	@get_kbd_line_10


@prev_line:				;Get previous history line
	mov	ax,offset DGROUP:hist_back
@history_line:
	call	ax
	mov	ax,lastchar
	mov	dot,ax			;Leave cursor at end of line
@history_search:
	jnc	@history_line_done	;Line found
	call	near ptr bell		;No line
@history_line_done:
	jmp	@get_kbd_line_10

@next_line:				;Get next history line
	mov	ax,offset DGROUP:hist_fwd
	jmp	short @history_line

@search_back:				;Search back thru history buffer
	mov	ax,offset DGROUP:hist_bck_match
@search:
	mov	di,dot			;Save current dot
	call	ax
	mov	dot,di			;Restore it
	jmp	short @history_search

@search_forw:				;Search forward thru history buffer
	mov	ax,offset DGROUP:hist_fwd_match
	jmp	short @search


@filename:				;Try to find a matching filename
	call	near ptr match_file
	cmp	auto_recall_on,1	;Autorecall ongoing?
	je	short @del_eol		;Yes, then delete to end of line
	jmp	@get_kbd_line_10

@del_left:				;Delete char before cursor
	mov	ax,offset dgroup:char_left
	jmp	short @delete

@del_right:				;Delete char at cursor
	mov	ax,offset dgroup:char_right
	jmp	short @delete


@del_prev_word:				;Delete upto word beginning
	mov	ax,offset dgroup:word_left
	jmp	short @delete

@del_next_word:				;Delete to start of next word
	mov	ax,offset dgroup:word_right
	jmp	short @delete

@del_bol:				;Delete to beginning of line
	mov	ax,offset dgroup:go_bol
	jmp	short @delete


@toggle_insert:
	mov	ax,1
	xor	al,edit_mode		;Toggle edit mode
	mov	edit_mode,al
	xchg	ax,bx
	add	bx,bx			;Word offset
	mov	cx,omode_cursor[bx]	;cx<-cursor shape
	mov	ah,01h			;Set cursor size function
	IF	TOGGLE_CURSOR
	int	10h			;BIOS 
	ENDIF
	jmp	@get_kbd_line_10	;Repeat editing loop

@abort_and_store:			;Erases current line but remembers
;					 it in the history buffer
	call	near ptr remember
;	Fall through to erase line
@erase_line:
	call	near ptr go_bol		;Move dot to beginning of line
;	fall thru to delete to end of line

@del_eol:				;Delete to end of line
	mov	ax,offset dgroup:go_eol

@delete:				;General purpose delete
	mov	si,dot			;Remember the dot
	call	ax			;Move dot to new position
	xchg	si,ax
	call	near ptr erase_to_dot	;Delete characters between ax and dot
	jmp	@get_kbd_line_10

@autorecall:
	xor	auto_recall,1		;Toggle auto recall mode
	jmp	@get_kbd_line_10

@vars:
; Expand the variables on the line.
	call	near ptr expand_var
	jmp	@get_kbd_line_10	;Ignore return status from expand_var


@inline_back:
@inline_forw:
@ignore:
	jmp	@get_kbd_line_10	;Ignore the character


ctrl_key_table	LABEL	WORD
	dw	@ignore		;NUL or ^@
	dw	@bol		;^A
	dw	@char_left	;^B
	dw	@ignore		;^C
	dw	@del_right	;^D
	dw	@eol		;^E
	dw	@char_right	;^F
	dw	@abort_and_store ;^G
	dw	@del_left	;^H
	dw	@filename	;^I
	dw	@vars		;^J might have to be @ignore in order for input 
;				 redirection to work properly but try for
;				 var expansion anyway.
	dw	@del_eol	;^K
	dw	@del_prev_word	;^L
	dw	@done_editing	;^M
	dw	@next_line	;^N
	dw	@del_eol_exec	;^O
	dw	@ignore		;^P - don't use, filtered by get key call ?
	dw	@quote		;^Q
	dw	@search_back	;^R
	dw	@ignore		;^S - don't use, does not work properly
	dw	@ignore		;^T
	dw	@prev_line	;^U
	dw	@search_forw	;^V
	dw	@del_next_word	;^W
	dw	@del_bol	;^X
	dw	@autorecall	;^Y
	dw	@default_action	;^Z
	dw	@erase_line	;^[ or ESC
	dw	@inline_back	;^\
	dw	@inline_forw	;^]
	dw	@done_wipeout	;^^
	dw	@ignore		;^_

cmd_key_table:				;table of command keys
	dw	127	;DEL
	dw	327	;HOME
	dw	328	;UP
	dw	331	;LEFT
	dw	333	;RIGHT
	dw	335	;END
	dw	336	;DOWN
	dw	338	;INS
	dw	339	;KDEL
	dw	371	;CTLLEFT
	dw	372	;CTLRIGHT
cmd_key_jumps:
	dw	@del_left	;DEL
	dw	@bol		;HOME
	dw	@prev_line	;UP
	dw	@char_left	;LEFT
	dw	@char_right	;RIGHT
	dw	@eol		;END
	dw	@next_line	;DOWN
	dw	@toggle_insert	;INS
	dw	@del_right	;KDEL
	dw	@word_left	;CTLLEFT
	dw	@word_right	;CTLRIGHT


@sfn_key:
; A shifted function key has been struck.
; (Treat same as an unshifted function key).
@fn_key:
; A function key has been struck.
	call	near ptr expand_fnkey
	jc	@fn_key_20			;Error or no expansion
	or	dx,dx				;Line to be executed
;						 immediately ?
	je	short @done_editing_2		;Yes, all done
@fn_key_20:
	jmp	@get_kbd_line_10		;else keep editing

@del_eol_exec:				;Deletes characters from the dot
;					 to end of the line and then
;					 executes the line
	mov	si,dot			;Remember the dot
	call	go_eol
	xchg	si,ax
	call	near ptr erase_to_dot
	jmp	short @done_editing


@done_wipeout:
; The line is executed. However it is not stored in the history buffer and
; is also removed from the screen (this is a little klugy).
	mov	ax,offset DGROUP:linebuf
	mov	dot,ax
	mov	dx,lastchar
	mov	lastchar,ax			;Temporarily pretend line
;						 is empty
	xchg	ax,dx
	push	ax
	call	near ptr set_disp_marks
	call	disp_line
	pop	ax
	mov	lastchar,ax			;Restore line length
	jmp	short @get_kbd_line_90

@done_editing:
	call	near ptr remember	;Store in history buffer
;					 Picks up line from global linebuf
@done_editing_2:
	call	near ptr disp_line	;Necessry for @del_eol_exec,
;					 might as well do for others
	mov	ax,lastchar
	mov	dot,ax			;Set dot to end of line
	call	near ptr line_to_scr	;Set cursor beyond last character

@get_kbd_line_90:
	@DispCh CR			;Do a carraige return because
;					 some applications like EDLIN
;					 only do a LF to go to next line
;	@DispCh LF			;Go onto next line
;	all done
@get_kbd_line_99:
	@restore
	ret
get_kbd_line endp



getkey proc near
	@GetKey	0
	mov	ah,0
	or	al,al
	jne	@114		;not '\0'
	mov	ah,0Bh
	int	21h		;check if key available
	or	al,al
	jz	@113		;no character available
	@GetKey	0
	mov	ah,1
	jmp	short @114
@113:
	xor	ax,ax
@114:
	ret
getkey	endp



;+
; FUNCTION : expand_fnkey
;
;	Inserts the expansion corresponding to a symbol key into the
;	linebuffer. If the buffer is too small, only as many characters as
;	possible are inserted and the function returns an error. The
;	function also returns an error if the symbols is not defined. The
;	function also updates the displayed line. The function also checks
;	if the line is to be executed immediately or not, based on the last
;	character of the fn key expansion.
;
; Parameters:
;	AX	= function key character, must be between (256+59)-(256+68)
;		  function keys and (256+84)-(256+93) for shifted functin keys.
;
; Returns:
;	CF	= 0 if no error, else 1
;		  If CF is 1, then AX is 0 if symbol not found or non-zero
;		  if symbol found but no room in line.
;	AX	= 1 if symbol was present 
;		  0 if symbol was not found.
;	DX	= 0 if the line is to be executed immediately
;		  non-0 otherwise
;		  DX is only valid if CF=0 and AX=1
; Register(s) destroyed:
; 	<TBA>
;-
expand_fnkey proc near
	@save	si,di
	push	bp
	mov	bp,sp
	sub	sp,LINEBUF_SIZE
exp_buf	equ <byte ptr [bp-LINEBUF_SIZE]>

	mov	si,offset DGROUP:fnkey_tab	;SI->'SFn'
	mov	dx,3				;DX<-length of string
	mov	di,si
	sub	ax,256+59
	cmp	ax,10				;Is it a function key
	jnb	@expand_fnkey_10		;No, shifted function key
	inc	si				;SI->'Fn'
	dec	dx				;length of string is 2
	jmp	short @expand_fnkey_15
@expand_fnkey_10:
	sub	ax,84-59
@expand_fnkey_15:
	cmp	ax,9				;F10 must appear as F0
	jne	@expand_fnkey_20
	mov	al,'0'-'1'
@expand_fnkey_20:
	add	al,'1'
	mov	2[di],al			;Store in 'SFn' string
; OK now try and expand the symbol.
;						 SI->symbol
	mov	ax,LINEBUF_SIZE
	xchg	ax,dx				;AX<-length of synbol
;						 DX<-length of expansion buffer
	lea	di,exp_buf
	call	near ptr get_symbol		;Params SI,AX,DI,DX
	jc	@expand_fnkey_99		;No symbol (buffer too
;						 small case not possible).
;						 AX will be 0 for this case.
; OK now we have the symbol expansion, so try insert it into the linebuffer.
;	AX contains length of expansion
	mov	si,di				;SI->expansion
	add	di,ax
	dec	di				;DI->last char of expansion
	xor	dx,dx
	mov	dl,byte ptr [di]		
	sub	dl,fnkey_exec_char		;Is this line to be
;						 immediately executed
	jne	@expand_fnkey_80		;No
	dec	ax				;The last char of expansion
;						 is a special char. Do not
;						 include it in the insert
;						 string 
@expand_fnkey_80:
	push	dx
	call	near ptr insert_at_dot		;Params SI,AX
;	pushf					;Save CF
;	call	disp_line
;	popf					;Restore CF
	mov	ax,1				;AX<-exit code
	pop	dx				;DX is 0 if line is to be
;						 executed immediately
@expand_fnkey_99:
	mov	sp,bp
	pop	bp
	@restore
	ret

expand_fnkey endp


;+
; FUNCTION : store_char
;
;	Stores	the character in AX into the line buffer if max line
;	length will not be exceeded. Characters may be inserted or
;	overwrite chars in the buffer depending on the edit mode and whether
;	auto-recall is on.
;
; Parameters:
;	AX	= character
;
; Returns:
;	CF	= 0 if no error, else 1
; Register(s) destroyed:
; 	<TBA>
;-
store_char proc near
	@save	si
	cmp	ax,256			;char >= 256
	jnb	@store_char_90		;Ignore if so
	cmp	edit_mode,0		;1 if insert mode
	je	@store_char_20		;Jump if overtype mode
	cmp	auto_recall_on,1	;Is auto-recall on ?
	je	@store_char_20		;Yes, behave as if in overtype mode
	mov	si,offset dgroup:tempchar ;temporary storage
	mov	[si],al			;Store char
	mov	ax,1			;Length of string
	call	near ptr insert_at_dot	;Insert the character
	jnc	@store_char_99		;No problemo
	jmp	short @store_char_90	;No room in buffer
@store_char_20:
	mov	si,dot			;Current position in line
	cmp	si,LINEBUF_END		;At line end?
	jae	@store_char_90
;	Enough room for a char
	mov	[si],al			;Store the char
	mov	dx,si			;DX->changed character
	mov	ax,si
	inc	ax			;AX->char after change
	mov	dot,ax			;Store it as new dot position
	cmp	si,lastchar		;Was it end of line ?
	jb	@store_char_30
	mov	lastchar,ax		;Store new end of line
@store_char_30:
	call	near ptr set_disp_marks	;ax,dx parameters
	clc				;Indicate no error
	jmp	short @store_char_99
@store_char_90:
	call	near ptr bell
	stc				;Indicate error
@store_char_99:
	@restore
	ret
store_char endp


;+
; FUNCTION : word_left
;
;	Move one word left.
;
; Parameters:
;	Globals linebuf and dot.
;
; Returns:
;	CF = 1 if dot was at beginning of the line already
;	     0 otherwise.
; Register(s) destroyed:
;-
word_left proc near
@word_left_10:
	call	near ptr char_left	;Move one char left
	jc	@word_left_99		;Already at beginning of line
@word_left_12:
	;Loop to backup over non-alphanum
	call	near ptr isalphnum	;AL alphanumeric?
	jnc	@word_left_15		;Yes
	call	near ptr char_left	;One char left.
					;AL<-char at dot
	jnc	@word_left_12		;Not at beginning of line
@word_left_15:				;Now backup to beginning of word
;	At this point, dot is always at a alphabetic char or beginning
;	of the line
	call	near ptr char_left
	jc	@word_left_98		;Were already at beginning of line
	call	near ptr isalphnum	;Alphanumeric?
	jnc	@word_left_15		;Yes, loop back
					;Found non-alphanumeric char
	call	near ptr char_right	;move over one
@word_left_98:
	clc				;Clear carry flag
@word_left_99:
	ret
word_left endp



;+
; FUNCTION : word_right
;
;	Move one word right.
;
; Parameters:
;	Globals linebuf and dot.
;
; Returns:
;	Nothing.
; Register(s) destroyed:
;-
word_right proc near
@word_right_10:				;Loop fwd over alphanumerics
	call	near ptr char_right	;One char right
					;AL<-char at dot
	jc	@word_right_99		;Already at end of line
	call	near ptr isalphnum	;Is AL alphanumeric ?
	jnc	@word_right_10		;Yes, loop back
@word_right_15:				;Now move to beginning of word
	call	near ptr char_right
	jc	@word_right_99		;Already at end of line
	call	near ptr isalphnum	;Alphanumeric?
	jc	@word_right_15		;Not alphanum char, loop back
@word_right_99:
	ret
word_right endp




;+
; FUNCTION : char_left
;
;	Moves the 'dot' one character to the left unless
;	already at the beginning of the line.
;
; Parameters:
;	Global	dot is current position in the line.
;
; Returns:
;	AL	= char at new dot position.
;	CF	= 1 if OLD dot was at beginning of line
;		  0 otherwise.
; Register(s) destroyed:
;-
char_left proc	near
	@save	si
	mov	ax,dot			;Current position in line
	cmp	ax,offset dgroup:linebuf
	jne	@char_left_5
	stc				;Dot already at beginning of line
	jmp	short @char_left_99
@char_left_5:
	dec	ax			;Move dot left
	mov	dot,ax
@char_left_99:
	xchg	ax,si
	lodsb				;AL<-char at dot
	@restore
	ret
char_left endp







;+
; FUNCTION : char_right
;
;	Moves the 'dot' one character to the right unless
;	already at the end of the line.
;
; Parameters:
;	Global	dot is current position in the line.
;
; Returns:
;	AL	= char at new dot position. Undefined if new dot is at
;		  the end of the line.
;	CF	= 1 if OLD dot was already at end of the line.
;		  0 otherwise.
; Register(s) destroyed:
;-
char_right proc	near
	@save	si
	mov	si,dot			;Current position in line
	mov	ax,lastchar		;AX->Beyond last char
	dec	ax			;AX->last char in line
	cmp	ax,si			;Dot at end ?
	jc	@char_right_99		;Already at line end
	inc	si			;Move dot right, CF not affected
	mov	dot,si
	lodsb				;AL<-char at dot, (undefined if
;					 dot is now at end of line).
;	CF	already clear
@char_right_99:
	@restore
	ret
char_right endp



;+
; FUNCTION : go_bol
;
;	Sets	dot to pint to the beginning of the line
;
; Register(s) destroyed: None.
;-
go_bol	proc	near
	mov	dot,offset dgroup:linebuf
	ret
go_bol	endp



;+
; FUNCTION : go_eol
;
;	Sets	dot to pint to the end of the line
;
; Register(s) destroyed: AX.
;-
go_eol	proc	near
	mov	ax,lastchar
	mov	dot,ax
	ret
go_eol	endp




;+
; FUNCTION : match_file
;
;	match_file tries to expand the filename to the left of the
;	cursor. If there are several matching files, the longest common
;	prefix is placed on the line.
;
; Parameters:
;	None.
;
; Returns:
;	Nothing.
; Register(s) destroyed:
;-
match_file proc	near
	@save	si,di
	push	bp
	mov	bp,sp
	sub	sp,64+44+2+2+2+2+2+2	;Locals
pname		equ	64		;Storage for entire filename with path
ffblk		equ	pname+44
ffblk_attr 	equ	ffblk-15h	;Attr byte within ffblk
ffblk_name 	equ	ffblk-1Eh	;filename within ffblk
fname		equ	ffblk+2		;Points to start of filename in name
oldDTAseg 	equ	fname+2
oldDTAoff 	equ	oldDTAseg+2
lineptr		equ	oldDTAoff+2	;Pointer to location in linebuf	
remaining	equ	lineptr+2	;Remembers remaining space in path
breakstate	equ	remaining+2	;Remembers break state

	mov	ax,3300h		;Get current break state
	int	21h			;DL<-current break state
	mov	byte ptr [bp-breakstate],dl ;Remember it
	xor	dl,dl
	mov	ax,3301h		;Disable break check during
;					 disk i/O
	int	21h
	lea	di,[bp-pname]		;OK 'cause SS==DS
	mov	si,dot			;Current line position
	mov	cx,63			;Max length of path
@match_file_10:
	cmp	si,offset dgroup:linebuf
	je	@match_file_51		;Beginning of line and filename
	dec	si
	mov	al,[si]			;AL<-next char
@match_file_25:
	call	near ptr isspace
	je	@match_file_50		;Beginning of filename
@match_file_45:				;Check for next char if
;					 pathname not too long
	loop	@match_file_10
	jmp	@match_file_99		;Pathname too long, just exit
@match_file_50:
	inc	si
@match_file_51:
;	Copy	filename into ASCIIZ buffer
;	SI->first char of filename in path buffer
	xor	dx,dx			;Flags
	mov	ax,63
	sub	ax,cx			;Length of name
	mov	[bp-remaining],cx	;remember num bytes left in
;					 path buffer
	xchg	ax,cx			;CX<-length of name
	mov	[bp-lineptr],si		;Save start of path name in linebuf
	mov	[bp-fname],di		;Remember start of filename in
;					 path buffer as well (assumed)
	or	cx,cx			;Too far for jcxz
	jne	@match_file_55
	jmp	@match_file_99		;No name, just exit
@match_file_55:
	lodsb				;AL<-next char
	stosb				;Store it
	cmp	al,'.'
	jne	@match_file_56
	or	dl,1			;Set flag to remember file type '.'
	jmp	short @match_file_65	;Check out next character
@match_file_56:
	cmp	al,'\'			;Directory separator?
	jne	@match_file_59
@match_file_57:
	mov	[bp-fname],di		;Update start of filename in
;					 path buffer
@match_file_58:
	and	dl,0FEh			;Forget file type flag
	jmp	short @match_file_65
@match_file_59:
	cmp	al,'/'			;Same thing
	je	@match_file_57
	cmp	al,':'			;Disk?
	jne	@match_file_65
	and	dl,0FEh			;Forget file type (shouldn't really 
;					 occur)
	mov	[bp-fname],di		;Update start of filename in
;					 path buffer

@match_file_65:
	loop	@match_file_55

	mov	cx,[bp-remaining]	;CX<-remaining space
	jcxz	@match_file_52		;Only space for terminator
	mov	al,'*'
	stosb				;Attach a '*'
	cmp	cl,3			;Enough space for ".*"
	jb	@match_file_52
	and	dl,1			;Saw a file type ?
	jne	@match_file_52		;Yes, go attach terminator
	mov	[di],2A2Eh		;Attach a ".*"
	inc	di
	inc	di
@match_file_52:
	xor	al,al			;AL<-0
	stosb				;Terminating 0

;	name	contains the filename
	push	es
	@GetDTA				;Get and save old DTA
	mov	[bp-oldDTAseg],es
	mov	[bp-oldDTAoff],bx
	pop	es
;
	lea	dx,[bp-ffblk]
	@SetDTA	dx			;Set DTA to our DTA

	lea	dx,[bp-pname]		;dx->ASCII name
	@GetFirst dx,16			;Include subdirs in search
	jnc	@match_file_70		;No error
	call	near ptr bell		;No files found
	jmp	@match_file_90		;Restore DTA and exit 


@match_file_70:				;At least one file found
	mov	di,[bp-fname]		;DI->filename portion
	lea	si,[bp-ffblk_name]	;Name returned by GetFirst
@match_file_71:				;Copy the file name
	lodsb
	stosb
	or	al,al
	jne	@match_file_71

	mov	al,SPACE		;If file, add a space
	test	byte ptr [bp-ffblk_attr],16 ;Subdirectory?
	je	@match_file_72		;No
	mov	al,'\'			;If subdir, add a backslash
@match_file_72:
	mov	byte ptr [di-1],al	;Add a space or a '\'
	mov	byte ptr [di],0

;	Now hunt for more files. For each file found, keep the longest 
;	prefix in common so far.

	@GetNext			;Get the next file name
	jc	@match_file_80		;No more files

	mov	di,[bp-fname]		;DI->start of file name
	lea	si,[bp-ffblk_name]	;Name returned by GetNext
	mov	cx,13			;Max Length of field
	repe	cmpsb			;Compare strings
	mov	byte ptr [di-1],0	;Terminating null
	jmp	short @match_file_72	;Try for more files

@match_file_80:
;	Found one or more files, copy the longest common prefix
;	into the line buffer
	mov	ax,[bp-lineptr]		;AX->start of chars to be deleted
	call	near ptr erase_to_dot	;Delete characters between dot
;					 and start of name
	lea	si,[bp-pname]		;SI->name to be copied
	mov	di,si
	xor	al,al
	mov	cx,64
	repne	scasb			;Hunt for terminating null
	mov	ax,63
	sub	ax,cx			;AX<-length of string
; SI->string, AX is length
	push	ax
	call	near ptr xlate_lower	;Convert to lowercase
	pop	ax	
	call	near ptr insert_at_dot	;SI->source, AX == length

@match_file_90:
	push	ds
	mov	ds,[bp-oldDTAseg]
	mov	dx,[bp-oldDTAoff]
	@SetDTA	dx			;Restore DTA
	pop	ds
@match_file_99:
; Restore previous state of break checking
	mov	dl,byte ptr [bp-breakstate]
	mov	ax,3301h
	int	21h
	mov	sp,bp
	pop	bp
	@restore
	ret
match_file endp



CSEG	ENDS

	END
[ RETURN TO DIRECTORY ]