mirror of
https://github.com/SEPPDROID/DR-DOS-OpenDOS.git
synced 2025-10-22 16:04:20 +00:00
542 lines
15 KiB
Plaintext
542 lines
15 KiB
Plaintext
; File : $HISTORY.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$
|
|
; ENDLOG
|
|
;
|
|
; DOSPLUS Command Line Editor Routines
|
|
;
|
|
|
|
include pcmode.equ
|
|
include i:msdos.equ
|
|
include i:char.def
|
|
include i:cmdline.equ
|
|
|
|
|
|
|
|
PCM_HISTORY CSEG
|
|
|
|
public init_history, save_history
|
|
public del_history_buffers, del_cur_history_buffer
|
|
public prev_cmd, next_cmd, match_cmd, search_cmd
|
|
public prev_word, next_word, match_word
|
|
public goto_eol
|
|
public del_bol
|
|
public deln_word
|
|
|
|
extrn next_char:near
|
|
extrn save_line:near
|
|
extrn space_out:near
|
|
extrn bs_out:near
|
|
extrn goto_bol:near
|
|
extrn del_eol:near
|
|
extrn del_line:near
|
|
extrn char_info:near
|
|
extrn put_string:near
|
|
extrn prev_w20:near
|
|
extrn deln_w10:near
|
|
|
|
init_history:
|
|
;------------
|
|
; On Entry:
|
|
; SS:BP -> history structure
|
|
; On Exit:
|
|
; SS:SP filled in with appropriate history buffer
|
|
; Preserve DX
|
|
;
|
|
push ds
|
|
push es
|
|
push ss
|
|
pop es
|
|
lea di,RL_HIST_SEG ; point at history buffer variables
|
|
mov si,offset histbuf1 ; assume root buffer
|
|
test RL_FLAGS,RLF_INROOT ; root task after all?
|
|
jnz init_history10 ; if not we need to switch
|
|
mov si,offset histbuf2 ; application buffer
|
|
init_history10:
|
|
movsw ; copy the seg
|
|
lodsw ; get overall length
|
|
sub ax,2*WORD ; last two words contain pointers
|
|
stosw ; save the working length
|
|
xchg ax,si ; SI -> saved buffer positions
|
|
mov ds,RL_HIST_SEG ; in the buffer segment
|
|
movsw ; update save and
|
|
movsw ; recall positions
|
|
pop es
|
|
pop ds
|
|
ret
|
|
|
|
|
|
save_history:
|
|
;------------
|
|
; We are exiting from READLINE - if line has been modified save the
|
|
; current line and update our pointers.
|
|
;
|
|
test dx,dx ; skip update of sav/recall pointers
|
|
jz save_history30 ; if empty line
|
|
call save_current ; save away current command line
|
|
mov di,RL_HIST_SAVE ; start with the old one
|
|
call find_current_hist ; wrap it if we need to
|
|
call find_next_null ; find end of "new" command
|
|
inc di ; onto next character
|
|
cmp di,RL_HIST_SIZE ; do we need to wrap ?
|
|
jb save_history10
|
|
xor di,di ; wrap the line
|
|
save_history10:
|
|
mov RL_HIST_SAVE,di ; update "save" pointer
|
|
test RL_FLAGS,RLF_DIRTY ; if a line has been changed we'd
|
|
jz save_history30 ; better update "recall" pointer
|
|
mov RL_HIST_RECL,di ; too
|
|
save_history30:
|
|
push ds
|
|
push es
|
|
push ss ! pop ds
|
|
lea si,RL_HIST_SAVE ; point at history buffer variables
|
|
mov es,RL_HIST_SEG
|
|
mov di,RL_HIST_SIZE
|
|
movsw ; save the "save" pointer
|
|
movsw ; and the "recall" one
|
|
pop es
|
|
pop ds
|
|
ret
|
|
|
|
save_current_if_dirty:
|
|
;---------------------
|
|
test RL_FLAGS,RLF_DIRTY ; if data is dirty (ie. user modified)
|
|
jz save_current20 ; then save it
|
|
save_current:
|
|
;------------
|
|
; Copy current buffer contents to next free location in history buffer
|
|
;
|
|
push si
|
|
push di
|
|
push dx
|
|
cmp dx,RL_HIST_SIZE ; is history buffer big enough ?
|
|
jae save_current10 ; no, skip saving this line
|
|
call find_free_hist ; Find the next bit of space
|
|
pop cx ! push cx ; CX = chars to save
|
|
jcxz save_current10 ; none, forget about it
|
|
push ds
|
|
push es
|
|
lds si,RL_BUFPTR
|
|
mov es,RL_HIST_SEG
|
|
rep movsb ; and save the data.
|
|
xor ax,ax ; Null terminate it.
|
|
stosb
|
|
pop es
|
|
pop ds
|
|
push di
|
|
call find_next_null ; Find the end of the entry we just
|
|
mov cx,di ; overwrote - if any remains zap it
|
|
pop di
|
|
cld
|
|
xor ax,ax ; Null terminate this entry
|
|
sub cx,di ; Figure out number of zeros to write
|
|
jbe save_current10
|
|
push es
|
|
mov es,RL_HIST_SEG
|
|
rep stosb ; and pad to the next entry
|
|
pop es
|
|
save_current10:
|
|
pop dx
|
|
pop di
|
|
pop si
|
|
save_current20:
|
|
ret
|
|
|
|
|
|
;
|
|
; Here is the code to deal with history buffers
|
|
;
|
|
; match_cmd Does a string search based on what the user has typed so far
|
|
; search_cmd Matches string, but doesn't change match state
|
|
; prev_cmd Retrieves the previous command from the history buffer
|
|
; next_cmd " " next " " " " "
|
|
;
|
|
match_cmd:
|
|
mov al,@hist_flg ; is matching enabled ?
|
|
and ax,RLF_MATCH
|
|
jz search_cmd ; no, always match
|
|
xor RL_FLAGS,ax ; toggle match bit
|
|
test RL_FLAGS,ax ; turning matching off ?
|
|
jz match30 ; then just exit
|
|
search_cmd:
|
|
mov RL_SAVPOS,si ; any chars to save ?
|
|
test si,si
|
|
jz prev_cmd ; blank line - just get previous
|
|
call save_current_if_dirty ; save current line if it needs saving
|
|
push si ; save current offset in line
|
|
push di
|
|
push dx ; Save index to end of current line
|
|
mov dx,si ; discard rest of line if no match
|
|
mov di,RL_HIST_RECL ; Point to the current entry
|
|
match_loop:
|
|
call find_prev_hist ; DI-> previous cmd in buffer
|
|
push di ; save this command position
|
|
mov di,RL_HIST_RECL ; get starting position
|
|
call find_current_hist ; handle any wrapping
|
|
pop si ; recover previous command
|
|
cmp di,si ; have we been all round ?
|
|
je match_error ; YES - We've looped so no match found
|
|
push ds
|
|
push si ; save this entry
|
|
mov ds,RL_HIST_SEG
|
|
mov di,RL_BUFOFF ; ES:DI -> match string
|
|
mov cx,RL_SAVPOS ; try to match CX characters
|
|
repe cmpsb
|
|
pop di ; recover this entry
|
|
pop ds
|
|
jne match_loop ; try again if we didn't match
|
|
call copy_from_hist ; if it did match copy it
|
|
match_error:
|
|
pop cx ; Get end of displayed line
|
|
pop di ; user's buffer
|
|
pop si
|
|
push si ; save current position in lines
|
|
call space_out ; Rub the displayed line out
|
|
call bs_out ; And backspace to it's start
|
|
call goto_eol ; display the line
|
|
call goto_bol ; Move cursor to begining
|
|
pop cx ; CX = end of matched portion
|
|
mov RL_SAVPOS,cx ; CX = Current position in command
|
|
jcxz match20
|
|
match10: ; Move cursor forward to current
|
|
push cx ; position
|
|
call next_char ; next_char destroys our loop count
|
|
pop cx ; so keep it
|
|
loop match10
|
|
match20:
|
|
mov cx,RL_SAVMAX ; we can't copy any more
|
|
mov RL_SAVPOS,cx
|
|
match30:
|
|
ret
|
|
|
|
|
|
prev_cmd:
|
|
; Get the previous command from the buffer
|
|
test RL_FLAGS,RLF_RECALLED ; is this the 1st recall ?
|
|
jnz prev_cmd10
|
|
push cx
|
|
push di
|
|
mov di,RL_HIST_SAVE ; get existing "save" ptr
|
|
call find_current_hist ; wrap it if we need to
|
|
mov RL_HIST_RECL,di ; and update "recall" pointer
|
|
pop di
|
|
pop cx
|
|
prev_cmd10:
|
|
call save_current_if_dirty ; save current line if it needs saving
|
|
call del_line ; then delete it
|
|
push si
|
|
push di
|
|
mov di,RL_HIST_RECL ; point to the current entry
|
|
call find_prev_hist ; DI -> previous entry
|
|
jmps found_cmd ; now go and copy it to the user buffer
|
|
|
|
next_cmd:
|
|
; Get the next command from the buffer
|
|
call save_current_if_dirty ; save current line if it needs saving
|
|
call del_line ; then delete it
|
|
push si
|
|
push di
|
|
mov di,RL_HIST_RECL ; point to the current entry
|
|
call find_next_hist ; DI -> next entry
|
|
; jmps found_cmd ; now go and copy it to the user buffer
|
|
|
|
found_cmd:
|
|
call copy_from_hist ; Copy from history to user buffer
|
|
pop di
|
|
pop si
|
|
; jmp goto_eol ; Display new line.
|
|
|
|
goto_eol: ; Move the cursor to the end of the
|
|
mov cx,dx ! sub cx,si ; displayed line
|
|
jcxz goto_e10 ; Already at the EOL
|
|
add si,RL_BUFOFF ; Get the Offset in the buffer
|
|
call put_string ; Output the sub_string
|
|
add di,cx ; Update the local variables
|
|
mov si,dx ; and return
|
|
goto_e10:
|
|
ret
|
|
|
|
; DI-> Current entry in history buffer
|
|
;
|
|
copy_from_hist:
|
|
or RL_FLAGS,RLF_RECALLED ; remember we have recalled something
|
|
and RL_FLAGS,not RLF_DIRTY ; this entry is already in buffers
|
|
mov RL_HIST_RECL,di ; update pointer for next time
|
|
call find_next_null ; how big is this entry ?
|
|
mov cx,di
|
|
mov si,RL_HIST_RECL
|
|
sub cx,si ; (CX-SI)=No of bytes to copy
|
|
cmp cx,RL_MAXLEN ; is the line bigger than our buffer ?
|
|
jb copy_from_hist1 ; if so we want to clip it
|
|
mov cx,RL_MAXLEN ; to the maximum possible value
|
|
copy_from_hist1:
|
|
mov dx,cx
|
|
push ds
|
|
mov ds,RL_HIST_SEG
|
|
mov di,RL_BUFOFF
|
|
rep movsb ; copy to line buffer
|
|
pop ds
|
|
call save_line ; save the line
|
|
mov cx,RL_SAVMAX ; we can't copy any more
|
|
mov RL_SAVPOS,cx
|
|
ret
|
|
|
|
; Some primitives for history buffer handling:
|
|
;
|
|
; find_free_hist Find the next free entry in the history buffer
|
|
; find_next_null Finds the end of the current entry
|
|
; find_prev_hist Finds the start of the previous entry
|
|
; find_next_hist Finds the start of the next entry
|
|
; find_current_hist Finds the start of current entry
|
|
|
|
;
|
|
; Make DI-> next entry of sufficient length for the current cmd buffer
|
|
; If we can't fit at the end zero the remainder of the buffer then wrap
|
|
; back to the start.
|
|
;
|
|
|
|
find_free_hist:
|
|
mov di,RL_HIST_SAVE ; Point at current last entry
|
|
push di ; Will DX bytes fit?
|
|
add di,dx
|
|
cmp di,RL_HIST_SIZE ; Did we run out of buffer?
|
|
pop di
|
|
jb find_free_hist20
|
|
xor ax,ax ; doesn't fit, so zero to end of buffer
|
|
find_free_hist10:
|
|
push es
|
|
mov es,RL_HIST_SEG
|
|
stosb ; keep on zeroing
|
|
pop es
|
|
cmp di,RL_HIST_SIZE
|
|
jb find_free_hist10
|
|
xor di,di ; wrap back to start of history buffer
|
|
find_free_hist20:
|
|
ret
|
|
|
|
; Entry DI-> entry in the history buffer
|
|
; Make DI-> next NULL in the history buffer ( end of current entry )
|
|
;
|
|
find_next_null:
|
|
mov cx,RL_HIST_SIZE
|
|
sub cx,di ; Calc no of bytes left
|
|
xor ax,ax ; Look for next terminator
|
|
push es
|
|
mov es,RL_HIST_SEG
|
|
repnz scasb
|
|
dec di ; point at the NUL
|
|
pop es
|
|
ret
|
|
|
|
; Entry DI-> entry in the history buffer
|
|
; Make DI-> previous entry in the history buffer
|
|
; Preserve CX
|
|
find_prev_hist:
|
|
call find_current_hist ; handle any wrapping
|
|
mov ax,di ; AX = entry we want previous for
|
|
find_prev_hist10:
|
|
push ax ; save current offset
|
|
push di ; save initial offset
|
|
xchg ax,di ; find next from here until
|
|
call find_next_hist ; we get back where we started
|
|
xchg ax,di ; AX = current offset
|
|
pop di ; recover initial value
|
|
pop bx ; recover previous offset
|
|
cmp ax,di ; have we wrapped yet ?
|
|
je find_prev_hist20 ; yes, BX = previous entry
|
|
ja find_prev_hist10 ; we are above target so continue
|
|
cmp bx,di ; we are below target - if previous
|
|
jae find_prev_hist10 ; was above target we are OK
|
|
cmp ax,bx ; if new below previous then we
|
|
jnb find_prev_hist10 ; have ERROR wrap, so stop
|
|
find_prev_hist20:
|
|
mov di,bx ; DI -> Previous entry
|
|
ret
|
|
|
|
find_next_hist:
|
|
call find_current_hist ; handle any wrapping
|
|
call find_next_null ; point to end of current entry
|
|
; jmp find_current_hist ; handle any wrapping
|
|
|
|
find_current_hist:
|
|
; This routine is complicated by the need to handle switchers, where
|
|
; our buffers and pointers may get out of step
|
|
push es
|
|
xor ax,ax
|
|
mov es,RL_HIST_SEG ; now work backwards to start of line
|
|
find_current_hist10:
|
|
cmp di,1 ; are we at the start of the buffer
|
|
jb find_current_hist30 ; then don't wrap
|
|
std
|
|
scasb ; is previous char a NUL ?
|
|
cld ; (it should be!)
|
|
jne find_current_hist10
|
|
inc di ; ES:DI -> 1st char of line
|
|
find_current_hist20:
|
|
mov cx,RL_HIST_SIZE
|
|
sub cx,di ; CX= Remaining no: of bytes in buffer
|
|
jbe find_current_hist30
|
|
repe scasb ; skip over zero's
|
|
jne find_current_hist40
|
|
find_current_hist30:
|
|
mov di,1 ; wrap to start of buffer
|
|
find_current_hist40:
|
|
dec di
|
|
pop es
|
|
ret
|
|
|
|
del_history_buffers:
|
|
;-------------------
|
|
; Delete contents of both history buffers
|
|
;
|
|
mov ax,histbuf1 ; Segment of 1st history buffer
|
|
mov cx,histsiz1 ; End of 1st history buffer
|
|
call zap_buffer
|
|
mov ax,histbuf2 ; Segment of 2nd history buffer
|
|
mov cx,histsiz2 ; End of 2nd history buffer
|
|
jmps zap_buffer
|
|
|
|
del_cur_history_buffer:
|
|
;----------------------
|
|
; Delete contents of the current history buffer
|
|
;
|
|
mov ax,RL_HIST_SEG ; zero the current buffer
|
|
mov cx,RL_HIST_SIZE
|
|
zap_buffer:
|
|
push es ; zero fill CX bytes at AX:0
|
|
push di
|
|
mov es,ax
|
|
xor ax,ax
|
|
mov RL_HIST_RECL,ax
|
|
mov RL_HIST_SAVE,ax
|
|
xor di,di
|
|
rep stosb ; zero fill the buffer
|
|
pop di
|
|
pop es
|
|
call del_line ; then delete current line
|
|
jmp save_line
|
|
|
|
|
|
next_word:
|
|
mov cx,dx ; Calculate the number of bytes
|
|
sub cx,si ; left to scan
|
|
jcxz next_w10 ; Skip if at the EOL
|
|
push si ; Save the current Index
|
|
mov si,di ; Scan from the current location
|
|
call match_word ; Get the next word boundary
|
|
mov cx,si
|
|
sub cx,di ; Calculate the string length
|
|
mov si,di ; to be displayed from the current
|
|
call put_string ; location and output the data
|
|
pop si
|
|
add si,cx ; Update the Index and Offset pointers
|
|
add di,cx
|
|
next_w10:
|
|
ret
|
|
|
|
;
|
|
; MATCH_WORD scans the buffer at ES:SI for word boundaries
|
|
; and returns to the calling routine whenever it detects such
|
|
; a boundary.
|
|
;
|
|
; On Entry: ES:SI Buffer Address
|
|
; CX Maximum No of bytes to Scan
|
|
;
|
|
; On Exit: ES:SI Next Word Boundary
|
|
;
|
|
match_word:
|
|
call char_info
|
|
test ah,CHAR_ALPHAN or CHAR_KANJI
|
|
loopnz match_word ; scan the rest of the current word
|
|
jcxz match_w30 ; end of word or kanji ?
|
|
|
|
push dx
|
|
match_w10:
|
|
mov dx,si
|
|
call char_info
|
|
test ah,CHAR_ALPHAN or CHAR_KANJI
|
|
loopz match_w10
|
|
jz match_w20
|
|
sub si,dx ; Correct the count in CX
|
|
add cx,si ; and return the location of the
|
|
mov si,dx ; word boundary
|
|
|
|
match_w20:
|
|
pop dx
|
|
match_w30:
|
|
ret
|
|
|
|
prev_word:
|
|
mov cx,si ! jcxz match_w30 ; Initialize the count
|
|
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_w10:
|
|
call match_word ; Find the next word boundary
|
|
jcxz prev_w15 ; Stop when we get to the current offset
|
|
mov dx,si ; Save current location
|
|
jmps prev_w10 ; and repeat
|
|
prev_w15:
|
|
jmp prev_w20
|
|
|
|
del_bol: ; Delete to the begining of the line
|
|
or si,si ! jz del_bol10 ; Ignore if at the begining
|
|
or dx,dx ! jz del_bol10 ; Or the line is empty
|
|
push di ; Save the current index
|
|
call goto_bol ; Move to the start of the line
|
|
pop cx ; Restore the current offset
|
|
jmp deln_w10 ; and jump to common code
|
|
del_bol10:
|
|
ret
|
|
|
|
deln_word:
|
|
mov cx,dx ; Calculate the number of bytes
|
|
sub cx,si ; left to scan
|
|
jcxz del_bol10 ; Skip if at the EOL
|
|
push si ; Save the current Index
|
|
mov si,di ; Scan from the current location
|
|
call match_word ; Get the next word boundary
|
|
mov cx,si
|
|
pop si
|
|
jmp deln_w10
|
|
|
|
PCMODE_DATA DSEG WORD
|
|
|
|
extrn @hist_flg:byte ; To select between histbufs 1 or 2
|
|
|
|
GLOBAL_DATA dseg word
|
|
|
|
extrn histbuf1:word, histsiz1:word, histbuf2:word, histsiz2:word
|
|
|
|
end
|
|
|