; HISTORY.ASM ; (c) 1989, 1990 Ashok P. Nadkarni ; ; This module implements the history feature of the cmdedit program. INCLUDE common.inc INCLUDE buffers.inc PUBLIC hist_top PUBLIC hist_init PUBLIC hist_fwd PUBLIC hist_back PUBLIC hist_fwd_match PUBLIC hist_bck_match PUBLIC remember PUBLIC hist_type PUBLIC execute_auto_recall PUBLIC execute_rsthist PUBLIC hist_ptr PUBLIC history CSEG SEGMENT PARA PUBLIC 'CODE' EXTRN linebuf:BYTE ;line buffer EXTRN lastchar:WORD EXTRN dot:WORD ;current position in line buffer ; The history stack is maintained as a string stack using the functions in ; the string stack library. The top and bottom of the stack are always ; zero length strings (the bottom is always a zero length sentinel ; implemented by the string stack package but the top is explicitly ; maintained by the history code. history $string_stack 2 DUP (<>) ;Buffer descriptors hist_ptr DW ? ;Ptr to current buffer descriptor DGROUP GROUP CSEG EXTRN bell:PROC EXTRN set_disp_marks:PROC EXTRN erase_to_dot:PROC ASSUME CS:DGROUP,DS:DGROUP,ES:DGROUP,SS:DGROUP ;+ ; FUNCTION : hist_init ; ; Initializes the various data structures associated with the ; command history buffers. CALLER MUST ENSURE PASSED ADDRESSES ARE VALID ; AND BUFFER IS LARGE ENOUGH. ; ; Parameters: ; AX - length of buffer in bytes ; BX - address of buffer ; CX - 0 if DOS buffer, any other value if application buffer ; ; Returns: ; Nothing. ; Registers destroyed : BX,CX ;- hist_init proc near push bx mov bx,offset DGROUP:history ;bx := address of buffer descriptors jcxz @hist_init_1 ;if DOS, bx OK add bx,TYPE $string_stack ;else point to application descriptor @hist_init_1: xchg ax,cx ;CX = buffer size pop ax ;AX = Buffer address ;BX points to appropriate descriptor call near ptr strstk_init ;Initialize buffer and descriptor xor al,al ;Push a zero length string onto stack mov cl,1 ;Force onto stack call near ptr strstk_push ;Assumes no errors ret hist_init endp ;+ ; FUNCTION : execute_rsthist execute_rsthist proc near mov bx,offset DGROUP:history ;bx := address of buffer descriptors jcxz @execute_rsthist_1 ;if DOS, bx OK add bx,TYPE $string_stack ;else point to application descriptor @execute_rsthist_1: call near ptr strstk_reset ;Initialize buffer and descriptor xor al,al ;Push a zero length string onto stack mov cl,1 ;Force onto stack call near ptr strstk_push ;Assumes no errors ret execute_rsthist endp ;+ ; FUNCTION : hist_type ; ; Sets the hist_ptr global to point at the descriptor for the ; current caller (DOS or application). ; ; Parameters: ; CX = 0 if DOS, anything else for application ; ; Returns: ; Nothing. ; Registers destroyed : BX ;- hist_type proc near mov bx,offset DGROUP:history ;address of first descriptor mov hist_ptr,bx ret hist_type endp ;+ ; FUNCTION : hist_top ; ; Resets the history pointer to the null string at top of history stack. ; The line buffer itslef is NOT changed. ; Parameters: ; None. ; ; Returns: ; Nothing. ; Registers destroyed: BX ;- hist_top proc near mov bx,hist_ptr ;Point to current descriptor call near ptr strstk_settop ;Set to top of history stack. ret hist_top endp ;+ ; FUNCTION : hist_back ; ; Gets the previous history line (if there is one) and stores it ; in the line buffer. Also makes it the current line. ; ; Parameters: ; None. ; ; Returns: ; Nothing. ; Registers destroyed: AX,BX,CX ;- hist_back proc near mov dot,offset DGROUP:linebuf jmp hist_bck_match ;Will return to caller hist_back endp ;+ ; FUNCTION : hist_fwd ; ; Gets the next history line (if there is one) and stores it ; in the line buffer. Also makes it the current line. ; ; Parameters: ; None. ; ; Returns: ; Nothing. ; Registers destroyed : ;- hist_fwd proc near mov dot,offset DGROUP:linebuf jmp hist_fwd_match ;Will return to caller hist_fwd endp ;+ ; ; FUNCTION : hist_copy ; ; Copies the current history line into the line buffer. The dot ; is set to the beginning of the line. ; ; Parameters: ; BX = points to the beginning of a line in the history buffer. ; ; Returns: ; Nothing. ; ; Registers destroyed: ;- hist_copy proc near mov bx,hist_ptr ;Current descriptor mov ax,offset DGROUP:linebuf ;the history line storage push ax ;Remember mov dot,ax ;Cursor will be at the beginning of line xchg ax,dx ;dx->first char in line (parameter) mov ax,lastchar ;ax->upper limit (parameter) call near ptr set_disp_marks ;Indicate changed range ;No registers destroyed mov ax,dx ;Restore beginning of line sub cx,LINEBUF_SIZE ;cx<-max length of line push cx ;Save it call near ptr strstk_copy ;Copy history line into linebuf ;AX == length of history string pop cx ; jnc @hist_copy_90 ;Jump if no error from strstk_copy call near ptr bell ;History line too long for buffer mov ax,cx ;Number of bytes = max length of line @hist_copy_90: pop dx ;dx->beginning of line add ax,dx ;ax->end mov lastchar,ax ;Update end of line call near ptr set_disp_marks ;Indicate changed range ;No registers destroyed ret hist_copy endp ;+ ; ; FUNCTION : remember ; ; Saves the line buffer in the history area. If the line already ; exists, it is moved to the top of the stack. ; ; Parameters: ; None. ; ; Returns: ; Nothing. ; ; Registers destroyed: ; AX,BX,CX,DX ;- remember proc near mov bx,hist_ptr ;Descriptor call near ptr strstk_settop ;Reset stack pointer mov ax,offset DGROUP:linebuf ;AX->line to be stored mov cx,lastchar sub cx,ax ;CX<-length of line call near ptr strstk_bck_find ;Look for it in history ; buffer. Note that if it is a ; null string, the sentinel at ; top of the stack is not found ; which is at it should be. jc @remember_10 ;Not in stack call near ptr strstk_kill ;If found delete it from stack @remember_10: call near ptr strstk_settop ;Point to sentinel at top of stack call near ptr strstk_kill ;Delete it mov dx,offset DGROUP:linebuf ;String to be remembered mov ax,lastchar ;Length of string sub ax,dx ;AX(AL)<-length of string mov cl,1 ;Force the push call near ptr strstk_push ;ignore success/fail status xor ax,ax ;Zero length sentinel mov cl,1 call near ptr strstk_push ;Push it ret remember endp ;+ ; ; FUNCTION : hist_fwd_match, hist_bck_match ; ; Looks fwd/back thru the history buffer starting from the current ; history location looking for a line whose first n chracters match ; the n characters before the current position in the line buffer. ; ; Parameters: ; None. ; ; Returns: ; CF = 0 if match found ; 1 if no match ; ; Registers destroyed: ;- hist_match proc near hist_fwd_match LABEL near mov dx,offset DGROUP:strstk_fwd_match jmp short @hist_match_10 hist_bck_match label near mov dx,offset DGROUP:strstk_bck_match @hist_match_10: mov bx,hist_ptr ;Descriptor address mov ax,offset DGROUP:linebuf ;start of pattern mov cx,dot ;Current position in linebuffer sub cx,ax ;Number of chars to match push cx ;Save push ax call dx ;Will set CF if no match, else sets ; 'current' to matched string pushf ;Remember flags jc @hist_match_20 ;No match call near ptr hist_copy ;Copy and display the history line ;Now reset it to original position @hist_match_20: popf ;Restore flags pop ax ;Line address pop cx ;Restore count pushf ;Save flags add ax,cx ;AX->dot mov dot,ax popf ;Restore flags jnc @hist_match_99 ;If match was found, exit mov ax,lastchar ;AX->end-of-line call erase_to_dot ;Else delete remaining chars in the ; line (chars between AX and dot) stc ;Indicate no match @hist_match_99: ret hist_match endp ;+ ; FUNCTION : execute_auto_recall ; ; This function looks backward through the history buffer for a ; line with a prefix that matches the characters to the left of ; the dot. The search begins with the CURRENT history line. ie. ; if the current history line has a matching prefix, the current ; pointer is not changed. If no match is found, the history stack ; is reset. ; ; Parameters: ; None. ; ; Returns: ; CF = 0 if match found ; 1 if no match ; Register(s) destroyed: ; AX,BX,CX,DX ;- execute_auto_recall proc near mov bx,hist_ptr ;BX->current history stack descriptor mov ax,offset DGROUP:linebuf ;AX->pattern mov cx,dot sub cx,ax ;CX<-length of pattern call near ptr strstk_prefix ;Current history line matches? jne @execute_auto_recall_50 ;No clc ;Yes, current line matches jmp short @execute_auto_recall_99 @execute_auto_recall_50: call near ptr hist_bck_match ;Search backward jnc @execute_auto_recall_99 ;Found a match ; No match, reset history stack pointer mov bx,hist_ptr ;BX->current history stack descriptor call near ptr hist_top ;Reset history stack stc ;Indicate no match @execute_auto_recall_99: ret execute_auto_recall endp CSEG ENDS END