; LSHERR.INC - Supplement to ZERR for manipulating LSH history file ; Date: 20 Aug 91 ; Rob Friefeld ; On a call to FIND_LSH, the shell stack is checked to see if LSH is the ; current shell. (If LSH has been renamed, the new name will have to be ; installed in LSHERR.) If so, the history file is opened and the error ; generating line identified before it is edited. Control then returns ; to the error handler. ; ; When the error handler is done, a call to FIX_LSH updates the history file. ; If the SAVE toggle for LSH is off, the history is NOT updated. ; ; The fixed-log and variable-log versions of LSH require different handling ; of the log file. Fixed-log must always be the same length. Error ; corrections which increase the size of the history may require that it ; be truncated. .accept 'Fixed Log LSH Version? (Y/N) ',fixlog ; ----- DEFINITIONS ------------------------------------------------------ dareadf equ 33 ; BDOS random read dawritef equ 34 ; BDOS random write darec equ fcb1+21h ; BDOS random record ; LSH keeps some needed information on the shell stack. ; - Log file name and location ; - Max size of log file ; - Write enable flag ; - Offset of current command line from file start o.fn equ 10h ; Shell stack entry offset to log file spec o.rrec equ 0eh ; In-mem recs of log file, on stack o.cptr equ 1dh ; Command pointer, word o.flag equ 1fh ; LSH flag byte in shell stack wrtbit equ 0 ; Write enable bit in LSH flag byte ext z3log,getsh2,gzmtop,setdma,initfcb ; ----- IDENTIFY LSH --------------------------------------------------- ; Examine shell stack for presence of LSH ; - Return NZ if no LSH (or writing OFF), LSH_FLAG NZ ; - Read history file ; - Match mcl to line in history ; - MCL will be upper case. If history is lower case, change mcl to match. ; find_lsh: call ckstack ; Go look at shell stack jr z,ok1 ; ID positive notok: or -1 ; Flag is NZ if any tests fail ok0: ld (lsh_flag),a ret ok1: call setbuf ; Set address for history file buffer call read_file ; Read the history file jr nz,notok ; Didn't find it ; Be sure there is no trailing blank before bad command delimiter ; Corrects a problem with ARUNZ when command not found ld hl,line-1 ok2: inc hl ld a,(hl) or a jr z,ok2a cp ';' jr nz,ok2 ok2a: push hl pop de ; HL -> delimiter dec de ; DE -> before delimiter ld a,(de) cp ' ' jr nz,ok3 ; Hey, no problem ok2b: ld a,(hl) ; Copy line tail over blank or a ld (de),a inc hl inc de jr nz,ok2b ; Now check on current line ; Line may not be in history, e.g. if too small to save ok3: call get_cptr ; Get pointer to shell stack command offset ld e,(hl) ; Deref it inc hl ld d,(hl) ld hl,(filebuf) ; ..relative to start add hl,de ld (histptr),hl ; Save start of line ld de,line ; MCL copy to match call match_line jr nz,notok ; Line isn't there ld (linptr),hl ; Save start of match xor a jr ok0 ; String match, case insensitive, 7 bit ; Enter: HL -> starting point (history) ; DE -> string to match, 0 terminated (MCL) ; Return: Z if OK ; NZ if EOF before match ; HL -> start of match ; Copy source to destination so case and attributes the same ; match_line: call match ret z ; OK nxtcmd: ld a,(hl) inc hl and 7fh cp ';' jr z,match_line cp cr jr nz,nxtcmd or a ret match: push hl push de match1: ld a,(de) and 7fh or a jr z,matchx ; Done call caps ; Compare upper case to upper ld c,a ld a,(hl) and 7fh call caps cp c jr nz,matchx ldi ; Copy source to dest as we match jr match1 ; So far so good matchx: pop de pop hl ret ; Find out if LSH is on stack ; The stack entry should start with "DU:LSH" ; - Shell name must match installed string, LSH's current name without DU: ; - RET Z if OK ; ckstack: call getsh2 ; HL -> stack, DE = entry size ld a,':' ; Skip DU: push de pop bc cpir ret nz ; Not even a DU: here ld de,lshid ; 0 terminated ID to match ckloop: ld a,(hl) and 7fh ; Mask off any set file attributes ld c,a ld a,(de) or a jr z,ckstk1 ; OK, LSH is running cp c ret nz ; Oops, NOT OK inc hl inc de jr ckloop ckstk1: call getsh2 ; Point to LSH's flag byte ld de,o.flag add hl,de bit wrtbit,(hl) ; Test write bit (Z = not enabled) jr nz,ckstk2 ; Flip sense of test xor a dec a ret ckstk2: xor a ret ; Set file buffer address by size of LSH history file kept on stack ; (The Type4 buffer address need not be exact. Even the first few ; records of ZERR can be overwritten without harm.) ; setbuf: call getsh2 ; HL -> shell stack ld de,o.rrec ; Get records to read add hl,de ld a,(hl) or a jr nz,rdf1 ld a,8*5 ; Default 40 recs for older LSH vers rdf1: ld (read_recs),a ; Save for read routine if type4 ld e,0 ; Convert recs to bytes ld d,a ; DE = recs * 256 srl d ; /2 rr e ld hl,entry ; Subtract from ZERR load address or a sbc hl,de else ld hl,($memry) ; Get pointer to file buffer endif ; type4 ld (filebuf),hl ret ; ----- FIX LSH HISTORY FILE ------------------------------------------- ; Call here after dealing with command line. If edited and lsh_flag active, ; then load edited line into history text and write it. ; fix_lsh: ld a,(lsh_flag) ; Was history file ready to update? or a ret nz ; LSH not running call saveline call write_file ret ; ; Put line buffer into text ; - At previously saved line pointer, make a hole big enough for edited ; line. Move line buffer into the hole. Must handle case of deleted line. ; saveline: ld hl,(linptr) ; Start of current line call saveline0 ; Line count in BC jr z,saveline1 ; Entire line was deleted inc bc ; Inc count for cr,lf inc bc saveline1: push hl add hl,bc ; New start of next line ex de,hl ; DE - destination of block move pop hl call nxtline ; Find current start of next line push hl pop bc ; BC - source of block move call end_movm ; Make hole for edited line ret c ; Error ld hl,line ; Now move line buf into hole ld de,(linptr) ; Destination call getlc ; Line count to BC ret z ; Nothing to move ldir ex de,hl ; HL -> end of line text ld (hl),cr inc hl ld (hl),lf ret ; Line count of edited line to BC saveline0: call getlc ret nz dec hl ; In middle of line? ld a,(hl) inc hl cp lf ret z ; No, at start cp eof ret ; Get line count by looking for 0 terminator ; Return count in BC, Z if 0 length line getlc: push hl ld hl,line ld bc,00ffh getlc1: ld a,(hl) inc c ; Counter inc hl or a jr nz,getlc1 getlcx: ld a,c or a pop hl ret ; ; Move memory block ; Input: BC - start, HL - end, DE - dest ; Output: Block moved. Return C = error, Z = head move, NZ = tail move ; end_movm: call get_eof ; HL -> EOF movm: ; Compute block length xor a sbc hl,bc ; End-start ret c ; Error - end before start push hl ; Swap HL,BC push bc pop hl ; HL = start pop bc ; BC = block size ; Head move or tail move? push hl xor a sbc hl,de ; Start - dest pop hl jr c,movm1 ; Tail move ; Head move inc bc ; BC = # bytes to move ldir xor a ret ; Tail move movm1: add hl,bc ; Add length to start ex de,hl add hl,bc ; Add length to dest ex de,hl inc bc ; # bytes lddr ; Tail move xor a inc a ret ; Search file buffer for first EOF char. There MUST be one. get_eof: push bc ld hl,(filebuf) ld a,eof ld bc,0ffffh cpir dec hl pop bc ret ; ; Search for next line of text ; HL -> starting point ; Return, HL -> next line, Z on EOF ; nxtline: ld a,(hl) ; Look at char and 7fh cp eof ret z inc hl ; Bump pointer cp lf jr nz,nxtline ld a,(hl) or a ret ; ----- SUPPORT CODE FROM LSH ------------------------------------------ ; ACCESS ENVIRONMENT KEPT ON SHELL STACK ; ; Get log file spec address in HL get_cmdf: ld de,o.fn ptradd: push de call getsh2 pop de add hl,de ret ; Get current line pointer (offset from start of text) get_cptr: ld de,o.cptr jr ptradd lsh_err: xor a dec a ret ;------------------------------------------------------------------- ; READ COMMAND FILE ; ; - Move file spec from shell stack to FCB and open file ; - Read the file into its buffer ; - Close file ; read_file: call top_of_mem call get_file jp z,lsh_err ; Load text file ; - FCB is ready to go, file is open ; - Init record number for possible random access ; - If file larger than READ_RECS, start with random access read ; read_file1: ; ld hl,0 ; Initialize record number ; ld (rec_num),hl if [not fixlog] ld c,compszf ; Get file size call bdosfcb ld hl,(darec) ; File size in HL ld de,(read_recs) ; Records to read in DE xor a sbc hl,de ; Diff is place to start reading (if > 0) jr c,read_file2 ld (rec_num),hl ; Do a random read to set fcb for subsequent sequential access read_file2: ld hl,(filebuf) push hl call SETDMA ld hl,(rec_num) ; Either 0 or READ_RECS back from end ld (darec),hl ld c,dareadf ; Read a record to set up real read call bdosfcb pop hl ; Now read sequential ld a,readf call r$wtext jp nz,lsh_err ; No go call close_file xor a ret else ; fixlog ld hl,(filebuf) ld bc,(read_recs) ld a,readf call r$wdata ; No read error on short file inc bc ld b,c ; Records left to read * 128 ld c,0 srl b rr c read_file3b: ld (hl),eof ; Truncate/fill file inc hl dec bc ld a,b or c jr nz,read_file3b call close_file xor a ret endif ; not fixlog ; Move fcb in shell stack to command line and open file ; Error exit if file doesn't exist ; stk_to_fcb: ld de,fcb call INITFCB call get_cmdf ; Command file spec @ HL ld de,fcb ; ... to fcb ld bc,12 ldir inc de ; Put user # ldi ret get_file: call stk_to_fcb ; Load FCB from shell stack file spec call open_file ; Open the file ret ;----------------------------------------------------------------- ; WRITE THE COMMAND FILE ; - Is writing enabled? ; - If so, update log file ; - If file size larger than amount read, add buffer to it ; Else replace file ; write_file: call get_file ; Open file on shell stack if [not fixlog] ld hl,(rec_num) ; Initial record number from read operation ld a,h or l jr z,write_file3 ; Rewrite the whole thing ; Use a random write to set up for sequential write of buffer ; ld (darec),hl ; Record to start with ld c,dawritef call bdosfcb ; Write a record of trash jr write_file4 ; Now set for real write write_file3: call make_file ; Erase old file, make new one jp z,lsh_err ; Sequential write from here ; write_file4: ld hl,(filebuf) write_to_file: ld a,writef ; Select write call r$wtext call close_file xor a ret else ; fixlog call get_eof ; HL -> current EOF ld de,(read_recs) ; Get nominal file size in DE ld b,7 mlt128: sla e ; ..records * 128 rl d djnz mlt128 ; DE = max text size or a sbc hl,de ; HL = EOF - max size ld de,(filebuf) sbc hl,de ; HL = min start - actual start jr c,write_file3 ; OK, room for whole thing ; Problem: existing log is larger than allowed ; - Write as much of the end as possible ; - Update line pointer ; ; filebuf edindex eof ; v v v ; DE/ \...HL../ \ ... read_recs ... / ; write_file2: inc hl ; Make sure to include 1AH terminator push hl ; Save delete length add hl,de ; HL -> new start call wnxtline ; Adjust to line beginning ld (filebuf),hl ; Write will start here pop hl ; Delete length add hl,bc ; ..plus chars advanced ld de,(histptr) ; Line sequence pointer ex de,hl or a sbc hl,de ; Line pointer - amount lopped off start push hl pop bc call get_cptr ; Restuff it ld (hl),c inc hl ld (hl),b jr write_file3 ; Adjust HL to line start ; Return HL -> line, BC = chars advanced to find it ; wnxtline: ld bc,-1 dec hl ; May happen to be at line start wnxtline1: ld a,(hl) and 7fh cp 1ah ret z inc hl inc bc cp lf jr nz,wnxtline1 ret ; Write the buffer ; write_file3: ld hl,(filebuf) ld a,writef ld bc,(read_recs) call r$wdata push af call close_file pop af jp nz,lsh_err ret endif ; not fixlog ;----------------------------------------------------------------- ; FILE I/O ; - Open, make, close a file in FCB1 ; - Read/ write a text file ; - Clear FCB1 ; ; Open file in fcb1 ; open_file: ld de,fcb call Z3LOG ld c,openf godosf: call bdosfcb inc a ret ; Z = No file, no room, etc. ; Make new file from fcb1 ; - DU already logged ; make_file: ld de,fcb call INITFCB ld c,erasef call bdosfcb ld c,makef ; Make new file jr godosf ; Close file in fcb1 ; close_file: ld c,closef ; Call BDOS with DE -> FCB ; bdosfcb: ld de,fcb jp bdos if [not fixlog] ; READ/WRITE A TEXT FILE (1AH TERMINATION) ; Enter: HL -> start ; A = WRITEF or READF ; File in FCB1 is open ; Location MEM_TOP contains overflow address ; Return: NZ = error, HL -> EOF ; r$wtext: ld (r$wtxtf),a ; Poke function r$wtxt1: push hl ; Current DMA address ld de,128 ; Look ahead to where next record will finish add hl,de ld de,(mem_top) ; Compare to over flow address or a ex de,hl sbc hl,de pop hl ret c ; will ovfl, ret NZ push hl ; Current DMA address call SETDMA r$wtxtf equ $+1 ld c,readf ; Execute selected function call bdosfcb pop hl ; Start of record just read or a ret nz ; Error ld bc,128 ; One record search ld a,eof ; Did last record contain an EOF? cpir ; Compare each character with EOF jr nz,r$wtxt1 ; And get next record if not ; xor a ret else ; not fixlog ; ; READ/WRITE A DATA FILE ; Enter: HL -> start ; BC = number of records, caller's responsibility to avoid ovfl ; A = WRITEF or READF ; File in FCB1 is open ; Return: NZ = error r$wdata: ld (r$wdatf),a r$wdat1: ld a,c ; BC record counter or b ret z ; done dec bc push bc ; save count push hl ; save dma addr call SETDMA ; set it r$wdatf equ $+1 ld c,readf ; execute selected function call bdosfcb pop hl ; restore dma pop bc ; restore count or a ret nz ; EOF on read ld de,128 add hl,de ; Point to next record jr r$wdat1 endif ; not fixlog ; Set up Top of Memory Pointer top_of_mem: if type4 ld hl,entry else call GZMTOP endif ; type4 ld (mem_top),hl ret ; ----- DATA ------------------------------------------------------------ rec_num: dw 0 ; Random access record read_recs: dw 0 dseg filebuf: ds 2 ; Pointer to history file buffer fstring: ds 2 ; Pointer to string to match lsh_flag: ds 1 ; LSH running or not (NZ = NO) mem_top: ds 2 ; Top of free mem address linptr: ds 2 ; Text pointer histptr: ds 2 ; History pointer cseg ; END LSHERR.INC