; $VER: alertd.asm 1.1 (03.04.98)
; by Kyzer/CSG
; usage: alertd <command to run when an alert occurs>
; remove with ctrl-c

	include	dos/dos.i
	include	exec/libraries.i
	include	exec/nodes.i
	include	exec/ports.i
	include	lvo/exec_lib.i
	include	lvo/dos_lib.i

; A3 = myport (sigtask/sigbit)
; A4 = command line
; A5 = dosbase
; A6 = execbase
; D7 = sigbit

	move.l	4.w,a6			; A6 = execbase
	cmp.w	#36,LIB_VERSION(a6)	; ensure 2.0+
	bcs	.quit

; prepare args
	move.l	a0,a4			; A4 = command line
	subq.l	#1,d0
	beq	.quit			; quit if no command
	clr.b	0(a4,d0.w)		; null-terminate cmdline

;---------------------------------------
; If we find ourselves already installed,
; we should signal the other one of us to quit.

	jsr	_LVOForbid(a6)
	lea	alertd(pc),a1
	jsr	_LVOFindPort(a6)
	tst.l	d0
	beq.s	.begin
	move.l	d0,a0
	move.l	MP_SIGTASK(a0),a1
	move.l	#SIGBREAKF_CTRL_C,d0
	jsr	_LVOSignal(a6)
.begin	jsr	_LVOPermit(a6)

; open dos
	lea	dosname(pc),a1
	moveq	#36,d0
	jsr	_LVOOpenLibrary(a6)
	tst.l	d0
	beq	.quit
	move.l	d0,a5			; A5 = dosbase

	jsr	_LVOCreateMsgPort(a6)
	tst.l	d0
	beq	.noport
	move.l	d0,a3			; A3 = myport
	;
	move.l	MP_SIGTASK(a3),d0
	lea	task(pc),a0
	move.l	d0,(a0)			; store task
	;
	move.b	MP_SIGBIT(a3),d7	; D7 = sigbit
	moveq	#1,d0
	asl.l	d7,d0
	lea	signal(pc),a0
	move.l	d0,(a0)			; store signal
	;
	lea	alertd(pc),a0
	move.l	a0,LN_NAME(a3)
	move.b	#-1,LN_PRI(a3)
	move.l	a3,a1
	jsr	_LVOAddPort(a6)		; make port public


; install patch
	lea	usecnt(pc),a0		; usecnt = 0
	clr.w	(a0)
	jsr	_LVODisable(a6)
	lea	patch(pc),a0
	move.l	a0,d0
.reinst	bsr.s	.setf
	lea	alert(pc),a0
	move.l	d0,(a0)
	jsr	_LVOEnable(a6)

.main	move.l	signal(pc),d0
	or.l	#SIGBREAKF_CTRL_C,d0
	jsr	_LVOWait(a6)		; wait for CTRL-C or sig from patch
	move.l	d0,d4
	btst	d7,d4
	beq.s	.notbit			; if it was from patch
	exg	a5,a6			; then execute command-line
	move.l	a4,d1
	moveq	#0,d2
	moveq	#0,d3
	jsr	_LVOExecute(a6)
	exg	a5,a6
.notbit	moveq	#SIGBREAKB_CTRL_C,d0	; quit if it was a CTRL-C signal
	btst	d0,d4
	beq.s	.main

.close	exg	a5,a6			; wait for usecnt to return to 0
	moveq	#1,d1
	jsr	_LVODelay(a6)
	exg	a5,a6
	move.w	usecnt(pc),d0
	bne.s	.close

; remove patch. if we don't remove _our_ patch, but someone else's,
; then put it back and return to the main loop again!
	jsr	_LVODisable(a6)
	move.l	alert(pc),d0
	bsr.s	.setf
	lea	patch(pc),a1
	cmp.l	d0,a1
	bne.s	.reinst
	jsr	_LVOEnable(a6)

; bye bye
	move.l	a3,a1
	jsr	_LVORemPort(a6)
	move.l	a3,a0
	jsr	_LVODeleteMsgPort(a6)
.noport	move.l	a5,a1
	jsr	_LVOCloseLibrary(a6)
.quit	moveq	#0,d0
	rts

; optimisation
.setf	move.w	#_LVOAlert,a0
	move.l	a6,a1
	jmp	_LVOSetFunction(a6)

;--------------------------------------
; The patch on Alert() itself

patch	lea	usecnt(pc),a0
	addq.w	#1,(a0)

	move.l	alert(pc),a0
	jsr	(a0)			; make the original alert
	move.l	task(pc),a1
	move.l	signal(pc),d0
	jsr	_LVOSignal(a6)		; signal maintask to run command

	lea	usecnt(pc),a0
	subq.w	#1,(a0)
	rts

usecnt	dc.w	0	; number of tasks currently calling Alert()
signal	dc.l	0	; signal-set to send to maintask
task	dc.l	0	; maintask
alert	dc.l	0	; correct address of Alert()

dosname	dc.b	'dos.library',0
	dc.b	'$VER: '
alertd	dc.b	'alertd 1.1',0		; name of public port
