**************************************************************************
*
* The GNO Shell Project
*
* Developed by:
*   Jawaid Bazyar
*   Tim Meekins
*
* $Id: history.asm,v 1.7 1998/12/21 23:57:06 tribby Exp $
*
**************************************************************************
*
* HISTORY.ASM
*   By Tim Meekins
*   Modified by Dave Tribby for GNO 2.0.6
*
* Routines for dealing with history buffers.
*
* History Data Structures:
*
*        HistoryPtr -> historyRec   [HistoryPtr is the most recent history]
*
* Where historyRec is:
*
*   [+0] NextHistory: pointer to historyRec
*   [+4] HistoryCmd:	 string of characters
*
* Note: text set up for tabs at col 16, 22, 41, 49, 57, 65
*              |     |                  |       |       |       |
*	^	^	^	^	^	^	
**************************************************************************
*
* Interfaces defined in this file:
*
* InsertHistory 	
*
* PrevHistory	
*
* NextHistory	
*
* SaveHistory	
*
* ReadHistory	
*
* InitHistory	
*
* PrintHistory	
*
**************************************************************************

	mcopy /obj/gno/bin/gsh/history.mac

dummyhistory	start		; ends up in .root
	end

	setcom 60

histNext	gequ	0
histCmd	gequ	4

;=========================================================================
;
; Add a C string to the head of the history buffer.
;
;=========================================================================

InsertHistory	START
	
	using HistoryData
	using global

ptr2	equ	0
ptr	equ	ptr2+4
len	equ	ptr+4
histvalptr	equ	len+2
space	equ	histvalptr+4

	subroutine (4:cmd),space

	pei	(cmd+2)
	pei	(cmd)
	jsr	cstrlen
	sta	len	 
	pea	0
	clc
	adc	#4+1
	pha
	~NEW
	sta	ptr
	stx	ptr+2
	ora	ptr+2
	beq	putdone

	inc	lasthist
	inc	numhist

	lda	historyptr	;Set up linked list pointers
	sta	[ptr]
	ldy	#histNext+2
	lda	historyptr+2
	sta	[ptr],y
	mv4	ptr,historyptr

	pei	(cmd+2)
	pei	(cmd)
	pei	(ptr+2)
	clc
	lda	ptr
	adc	#4
	pha
	jsr	copycstr
;
; Now, find out what the maximum history is and prune to that value
;
putdone	anop

;
; Get value of $HISTORY environment variable
;
	ph4	#historyStr
	jsl	getenv
	sta	histvalptr	Save pointer to GS/OS result buffer.
	stx	histvalptr+2
	ora	histvalptr+2	If buffer wasn't allocated,
	jeq	goback	 all done.
;
; Call Dec2Int to convert value of string into an integer
;
	pha		Reserve room on stack for result.
	lda	histvalptr	Get pointer to $HISTORY value.
	ldx	histvalptr+2
	clc		Add 4 to address to skip
	adc	#4	 over length words.
	bcc	pushaddr
	inx
pushaddr	phx		Push address onto stack.
	pha
	ldy	#2
	lda	[histvalptr],y
	pha		Push length.
	pea	0	Push signedFlag = 0 (unsigned)
	Tool	$280b	Dec2Int.
	pla		Get result.
	sta	size
	beq	alldone
;
; Follow linked list until we reach size histories
;
	mv4	historyptr,ptr
	ldy	#histNext+2
follow	lda	[ptr]
	tax
	ora	[ptr],y
	beq	alldone	;not enough
	dec	size
	beq	prune
	lda	[ptr],y
	sta	ptr+2
	stx	ptr
	bra	follow
;
; we have enough, start pruning
;
prune	lda	[ptr]
	sta	ptr2
	lda	[ptr],y
	sta	ptr2+2
	lda	#0
	sta	[ptr]
	sta	[ptr],y	  ;terminate last history
;
; Dispose remaining
;
dispose	lda	ptr2
	ora	ptr2+2
	beq	alldone
	dec	numhist
	lda	[ptr2]
	sta	ptr
	lda	[ptr2],y
	sta	ptr+2
	pei	(ptr2+2)
	pei	(ptr2)
	jsl	nullfree
	lda	ptr
	sta	ptr2
	lda	ptr+2
	sta	ptr2+2
	ldy	#histNext+2
	bra	dispose

alldone	pei	(histvalptr+2)
	pei	(histvalptr)
	jsl	nullfree

goback	return

size	ds	2

	END

;=========================================================================
;
; Places the previous history into the command buffer, called when pressing
; UP-ARROW.
;
;=========================================================================

PrevHistory	START

	using HistoryData
	using global
	using	termdata

	ora2	historyptr,historyptr+2,@a	 ;If no history, then skip
	jeq	ctl5a

	ldx	cmdloc	;Move cursor to beginning of line
	jsr	moveleft

	lda	cdcap
	ora	cdcap
	beq	ctl5b0
	tputs (cdcap,#0,#outc)
	bra	ctl5g

ctl5b0	ldx	cmdlen	;clear line
ctl5b	dex
	bmi	ctl5c
	phx
	lda	#' '
	jsr	putchar
	plx
	bra	ctl5b
ctl5c	ldx	cmdlen
	jsr	moveleft

ctl5g	ora2	currenthist,currenthist+2,@a
	bne	ctl5i	;If not set up for current hist then
	lda	historyptr+2	;Set up at start.
	ldx	historyptr
	ldy	#2
	bra	ctl5j

ctl5i	mv4	currenthist,0	;Otherwise move to previous history
	stz	cmdlen
	stz	cmdloc
	ldy	#HistNext+2
	lda	[0]
	tax
	lda	[0],y

ctl5j	sta	0+2	;Save some pointers
	stx	0
	sta	currenthist+2
	stx	currenthist
	ora	0	;If ptr is 0 then at end, quit
	beq	ctl5a

	ldx	#0	;Display and store command
	iny2
ctl5h	lda	[0],y
	and	#$FF
	sta	cmdline,x
	beq	ctl5ha
	inx
	iny
	bra	ctl5h
ctl5ha	stx	cmdlen
	stx	cmdloc

	ldx	#^cmdline
	lda	#cmdline
	jsr	puts

ctl5a	rts

	END

;=========================================================================
;
; Places the next history into the command buffer, called when pressing
; DOWN-ARROW.
;
;=========================================================================

NextHistory	START

	using HistoryData
	using global
	using	termdata

	ora2	historyptr,historyptr+2,@a	 ;No hist, then skip
	jeq	ctl6a

	stz	4          ;Walk through linked list searching
	stz	4+2        ; for hist prior to last hist.
	mv4	historyptr,0
ctl6i	if2	0,ne,currenthist,ctl6j
	if2	0+2,eq,currenthist+2,ctl6k
ctl6j	mv4	0,4
	ldy	#HistNext+2
	lda	[0]
	tax
	lda	[0],y
	sta	0+2
	stx	0
	bra	ctl6i

ctl6k	ldx	cmdloc	;Move cursor to left
	jsr	moveleft

	lda	cdcap
	ora	cdcap
	beq	ctl6b0
	tputs (cdcap,#0,#outc)
	bra	ctl6g

ctl6b0	ldx	cmdlen	;clear line
ctl6b	dex
	bmi	ctl6c
	phx
	lda	#' '
	jsr	putchar
	plx
	bra	ctl6b
ctl6c	ldx	cmdlen
	jsr	moveleft

ctl6g	stz	cmdlen	;Get hist info.
	stz	cmdloc
	mv4	4,currenthist
	ora2	4,4+2,@a
	beq	ctl6a	;Whoops, back to end, quit

	ldx	#0	;Output the new command
	ldy	#4
ctl6h	lda	[4],y
	and	#$ff
	sta	cmdline,x
	beq	ctl6ha
	iny
	inx
	bra	ctl6h
ctl6ha	stx	cmdlen
	stx	cmdloc

	ldx	#^cmdline
	lda	#cmdline
	jsr	puts

ctl6a	rts

	END

;=========================================================================
;
; Save History if variable set
;
;=========================================================================

SaveHistory	START

	using HistoryData
	using global

svhisvalptr	equ	0
space	equ	svhisvalptr+4

	subroutine ,space

	lda	historyFN
	sta	DestroyParm+2
	sta	CreateParm+2
	sta	OpenParm+4
	lda	historyFN+2
	sta	DestroyParm+4
	sta	CreateParm+4
	sta	OpenParm+6
;
; Get value of $SAVEHISTORY environment variable
;
	ph4	#savehistStr
	jsl	getenv
	sta	svhisvalptr	Save pointer to GS/OS result buffer.
	stx	svhisvalptr+2
	ora	svhisvalptr+2	If buffer wasn't allocated,
	jeq	goback	 all done.
;
; Call Dec2Int to convert value of string into an integer
;
	pha		Reserve room on stack for result.
	lda	svhisvalptr	Get pointer to $HISTORY value.
	ldx	svhisvalptr+2
	clc		Add 4 to address to skip
	adc	#4	 over length words.
	bcc	pushaddr
	inx
pushaddr	phx		Push address onto stack.
	pha
	ldy	#2
	lda	[svhisvalptr],y
	pha		Push length.
	pea	0	Push signedFlag = 0 (unsigned)
	Tool	$280b	Dec2Int
	pla		Get result.
	sta	size
	jeq	done
;
; Create and write history to file
;
	Destroy DestroyParm
	Create CreateParm
	jcs	done
	Open	OpenParm
	bcs	done
	mv2	OpenRef,(WriteRef,WriteCRRef,CloseRef)

loop1	mv4	historyptr,0
	mv2	size,count
	ldy	#histNext+2
loop2	lda	0
	ora	0+2
	beq	next
	lda	[0]
	tax
	dec	count
	beq	write
	lda	[0],y
	sta	0+2
	stx	0
	bra	loop2
write	clc
	lda	0
	adc	#4
	tax
	sta	WriteBuf
	lda	0+2
	adc	#0
	sta	WriteBuf+2
	pha
	phx
	jsr	cstrlen
	sta	WriteReq
	Write WriteParm
	bcs	doneclose
	Write WriteCR
	bcs	doneclose
next	dec	size
	bne	loop1

doneclose	Close CloseParm

done	pei	(svhisvalptr+2)	Free $SAVEHISTORY value buffer
	pei	(svhisvalptr)
	jsl	nullfree

goback	return


DestroyParm	dc	i2'1'
	dc	a4'historyFN'

CreateParm	dc	i2'3'
	dc	a4'historyFN'
	dc	i2'$C3'
	dc	i2'0'

OpenParm	dc	i2'2'
OpenRef	ds	2
	dc	a4'historyFN'

WriteParm	dc	i2'4'
WriteRef	ds	2
WriteBuf	dc	a4'buffer'
WriteReq	ds	4
	ds	4

WriteCR	dc	i2'4'
WriteCRRef	ds	2
	dc	a4'CRBuf'
	dc	i4'1'
	ds	4
CRBuf	dc	i1'13'

CloseParm	dc	i2'1'
CloseRef	ds	2

size	ds	2
count	ds	2

	END

;=========================================================================
;
; Read History file
;
;=========================================================================

ReadHistory	START

	using HistoryData
	using global

	lda	historyFN
	sta	OpenParm+4
	lda	historyFN+2
	sta	OpenParm+6

	lda	#0
	sta	historyptr
	sta	historyptr+2

	Open	OpenParm
	bcs	done
	mv2	OpenRef,(ReadRef,NLRef,CloseRef)
	NewLine NLParm
	bcs	doneclose

loop	anop
	Read	ReadParm
	bcs	doneclose
	ldy	ReadTrans
	beq	doneclose
	dey
	lda	#0
	sta	buffer,y
	ph4	#buffer
	jsl	InsertHistory
	bra	loop

doneclose	Close CloseParm

done	rts

OpenParm	dc	i2'2'
OpenRef	ds	2
	dc	a4'historyFN'

NLParm	dc	i2'4'
NLRef	ds	2
	dc	i2'$7F'
	dc	i2'1'
	dc	a4'NLTable'
NLTable	dc	i1'13'

ReadParm	dc	i2'4'
ReadRef	ds	2
	dc	a4'buffer'
	dc	i4'1024'
ReadTrans	ds	4

CloseParm	dc	i2'1'
CloseRef	ds	2

size	ds	2

	END

;=========================================================================
;
; Init History
;
;=========================================================================
InitHistory	START
	using	HistoryData

	ph4	#histName	Create string
	jsl	AppendHome	 $HOME/history
	stx	historyFN+2	Store pointer to it
	sta	historyFN	 in historyFN
	rts
	END

;=========================================================================
;
; Print History (this is the history command)
;
;=========================================================================

PrintHistory	START

	using HistoryData
	using global

ptr	equ	0
status	equ	ptr+4
space	equ	status+2

	subroutine (4:argv,2:argc),space

	stz	status

	lda	argc
	dec	a
	beq	chkptr

	ldx	#^usage
	lda	#usage
	jsr	errputs
	inc	status	Return status = 1.
	bra	done

chkptr	lda	historyptr
	ora	historyptr+2
	beq	done

	lda	numhist
	sta	num
loop1	mv4	historyptr,ptr
	lda	num
	bmi	done
	sta	count
loop2	lda	count
	beq	print
	ldy	#histNext+2
	lda	[ptr]
	tax
	lda	[ptr],y
	sta	ptr+2
	stx	ptr
	ora	ptr
	beq	next
	dec	count
	bra	loop2

print	sub2	lasthist,num,@a
	Int2Dec (@a,#numbstr,#4,#0)
	ldx	#^numbstr
	lda	#numbstr
	jsr	puts
	clc
	ldx	ptr+2
	lda	ptr
	adc	#4
ok	jsr	puts
	jsr	newline

next	dec	num
	bra	loop1

done	return 2:status

numbstr	dc	c'0000:  ',h'00'
num	ds	2
count	ds	2

usage	dc	c'Usage: history',h'0d00'

	END

;=========================================================================
;
; History Data
;
;=========================================================================

HistoryData	DATA

lasthist	dc	i2'0'
numhist	dc	i2'0'
currenthist	ds	4
historyptr	dc	i4'0'
historyStr	gsstr	'HISTORY'
savehistStr	gsstr	'SAVEHIST'
histName	dc	c'/history',i1'0'
historyFN	ds	4

	END
