; 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