*******************************************************************************
* OpenLibraryCounter.asm
*******************************************************************************
* Author: Peter Mattsson
* Created: 960505
* Last changed: 970722
*******************************************************************************
* Changes between V1.12ß and V1.13:
* Reading the log file is much safer.
*******************************************************************************
* TODO: Make the removal of the patch even safer.
*	Store what programs open what libraries.
*	Store the library names in a binary search tree to cut search times.
*******************************************************************************

		MACHINE	68000

		INCDIR	"INCLUDE:"
		INCLUDE	"lvos.i"
		INCLUDE	"dos/dos.i"
		INCLUDE	"exec/exec.i"

*******************************************************************************
* Macros
*******************************************************************************

CALL		MACRO
		Jsr	_LVO\1(A6)
		ENDM

main
	Bsr.S	Initialize
	Tst.L	d0
	Beq.S	.Failed

		Bsr.S	ReadArguments

		Bsr.S	LinkedListConstructor
		Move.L	userFileName,a0
		Bsr.S	LinkedListLoadListFromFile

		Move.L	$4.w,a6
		Move.L	a6,a1
		Move.W	#_LVOOpenLibrary,a0
		Move.L	#MyOpenLibrary,d0
		CALL	SetFunction
		Move.L	d0,oldOpenLibrary
.WaitForMessage
			MoveQ.L	#0,d1
			Move.L	myMessagePort,a0
			Move.B	MP_SIGBIT(a0),d1
			MoveQ.L	#1,d0
			Lsl.L	d1,d0
			Or.L	#SIGBREAKF_CTRL_C,d0
			Move.L	$4.w,a6
			CALL	Wait
			And.L	#SIGBREAKF_CTRL_C,d0
			Bne.S	.QuitProgram
.GetMoreMessages
			Move.L	myMessagePort,a0
			CALL	GetMsg
			Tst.L	d0
			Beq.S	.NoMessage
				Move.L	d0,a0
				Move.L	d0,-(SP)
				Lea	20(a0),a0
				Bsr.S	LinkedListUpdateLibrary
				Move.L	(SP)+,a1
				MoveQ.L	#0,d0
				Move.L	$4.w,a6
				Move.W	MN_LENGTH(a1),d0
				CALL	FreeMem
			Bra.S	.GetMoreMessages
.NoMessage
		Bra.S	.WaitForMessage
.QuitProgram
		Move.L	oldOpenLibrary,d0
		Move.W	#_LVOOpenLibrary,a0
		Move.L	$4.w,a6
		Move.L	a6,a1
		CALL	SetFunction

		Bsr.S	LinkedListDestructor

		Move.L	#500,d1
		Move.L	_DOSBase,a6
		CALL	Delay

.removeRemainingMessages
		Move.L	myMessagePort,a0
		Move.L	$4.w,a6
		CALL	GetMsg
		Tst.L	d0
		Beq.S	.noMoreMessages
			Move.L	d0,a1
			MoveQ.L	#0,d0
			Move.L	$4.w,a6
			Move.W	MN_LENGTH(a1),d0
			CALL	FreeMem
		Bra.S	.removeRemainingMessages
.noMoreMessages

.Failed
	Bsr.S	ShutDown
        MoveQ.L	#0,d0

	Rts

*******************************************************************************

MyOpenLibrary
	Move.L	a1,-(SP)
	Move.L	oldOpenLibrary,a0
	Jsr	(a0)
	Move.L	(SP)+,a0
	Tst.L	d0
	Beq.S	.NotOpened
	MoveM.L	d0/d2/a2,-(SP)

	Move.L	a0,a2
	Bsr.S	strlen
	Add.L	#14+6+1,d0
	Move.W	d0,d2
	MoveQ.L	#MEMF_ANY,d1
	CALL	AllocMem
	Tst.L	d0
	Beq.S	.NoMemory
		Move.L	a2,a0
		Move.L	d0,a1
		Clr.L	(a1)
		Clr.L	LN_PRED(a1)
		Clr.L	LN_NAME(a1)
		Clr.L	MN_REPLYPORT(a1)
		Move.W	d2,MN_LENGTH(a1)
		Clr.W	LN_TYPE(a1)
		Move.L	a1,a2
		Lea	20(a1),a1
		Bsr.S	strcpy
		Move.L	a2,a1
		Move.L	myMessagePort,a0
		CALL	PutMsg
.NoMemory
	MoveM.L	(SP)+,d0/d2/a2
.NotOpened
	Rts

*******************************************************************************
* Out : d0 - Nonzero if success

Initialize
	Clr.L	_DOSBase
	Clr.L	myMessagePort
	Bsr.S	OpenDOSLibrary
	Tst.L	d0
	Beq.S	.NoSuccess
	Bsr.S	CreateMessagePort
.NoSuccess
	Rts

*******************************************************************************
* Out : d0 - Nonzero if success

CreateMessagePort
	Move.L	$4.w,a6
	CALL	CreateMsgPort
	Move.L	d0,myMessagePort

	Rts

*******************************************************************************
* Out : d0 - Nonzero if success

OpenDOSLibrary
	Move.L	$4.w,a6
	Lea	DosName,a1
	MoveQ.L	#37,d0				; Requires KS 2.04
	CALL	OpenLibrary
	Move.L	d0,_DOSBase

	Rts

*******************************************************************************

ShutDown
	Move.L	readArgsStruct,d1
	Beq.S	.noArgsToFree

		Move.L	_DOSBase,a6
		CALL	FreeArgs
.noArgsToFree
	Tst.L	myMessagePort
	Beq.S	.NoMessagePortCreated
		Move.L	myMessagePort,a0
		Move.L	$4.w,a6
		CALL	DeleteMsgPort
.NoMessagePortCreated
	Tst.L	_DOSBase
	Beq.S	.NoDosOpened
		Move.L	_DOSBase,a1
		Move.L	$4.w,a6
		CALL	CloseLibrary
.NoDosOpened
	Rts

*******************************************************************************

ReadArguments
	Lea	ReadArgsTempl,a0
	Move.L	a0,d1
	Lea	readArgsArray,a0
	Clr.L	(a0)
	Clr.L	4(a0)
	Move.L	a0,d2
	MoveQ.L	#0,d3
	Move.L	_DOSBase,a6

	CALL	ReadArgs

	Move.L	d0,readArgsStruct
	Beq.S	.error

		Lea	readArgsArray,a0
		Tst.L	(a0)
		Bne.S	.filenameSpecified

		Move.L	#DefaultFileName,userFileName
		Bra.S	.checkInterval
.filenameSpecified
		Move.L	(a0),userFileName
.checkInterval
		Tst.L	4(a0)
		Beq.S	.noIntervalSpecified

		Move.L	4(a0),a0
		Move.L	(a0),saveInterval
		Bne.S	.intervalSpecified

.noIntervalSpecified
		Move.L	#500,saveInterval
.intervalSpecified
	Bra.S	.noError

.error
		Move.L	#DefaultFileName,userFileName
		Move.L	#500,saveInterval

.noError
	Rts

*******************************************************************************
* In : -
* Out : -

LinkedListConstructor
	Clr.L	head
	Move.L	saveInterval,numberOfOpens
	Rts

*******************************************************************************
* In : -
* Out : -

LinkedListDestructor
	Tst.L	head
	Beq.S	.headIsNull

		Move.L	userFileName,a0
		Move.L	head,a2
		Bsr.S	LinkedListSaveListToFile

		Move.L	head,a2
		Bsr.S	ListNodeDestructor
.headIsNull
	Rts

*******************************************************************************
* In : a0 - char* libraryName
* Out : -

LinkedListUpdateLibrary

	Move.L	a0,a1
	MoveQ.L	#0,d0
.checkIfNameIsLegal                             ; In this case, no TABs in the name and
                                                ; shorter than 1024 characters
	Cmp.W	#1024,d0
	Beq.S	.nameIsIllegal

	Cmp.B	#'\t',(a0)
	Bne.S	.noTab
	
.nameIsIllegal
	Lea	IllegalLibName,a0
	Bra.S	.nameWasIllegal

.noTab
	AddQ.W	#1,d0

	Tst.B	(a0)+
	Bne.S	.checkIfNameIsLegal
	
	Move.L	a1,a0

.nameWasIllegal

	Tst.L	head
	Beq.S	.headIsNull

		Move.L	head,a2
		Bsr.S	ListNodeUpdateLibrary

		SubQ.L	#1,numberOfOpens
		Bne.S	.notTimeToSave

			Move.L	userFileName,a0
			Bsr.S	LinkedListSaveListToFile

			Move.L	saveInterval,numberOfOpens
.notTimeToSave
		Bra.S	.headWasNotNull
.headIsNull
		Move.L	a0,a2

		MoveQ.L	#ListNodeSizeOf,d0
		MoveQ.L	#MEMF_ANY,d1
		Move.L	$4.w,a6
		CALL	AllocMem
		Move.L	d0,head
		Beq.S	.headWasNotNull

		Move.L	a2,a0
		Move.L	d0,a2
		MoveQ.L	#1,d0
		Bsr.S	ListNodeConstructor

		Tst.L	d0
		Bne.S	.successful

		Clr.L	head
		Bra.S	.noSuccess
.successful
		SubQ.L	#1,numberOfOpens
.headWasNotNull
.noSuccess
	MoveQ.L	#0,d0
	Rts

*******************************************************************************
* In : a0 - char* fileName
* Out : -

LinkedListSaveListToFile
	Tst.L	head
	Beq.S	.headIsNull

		Move.L	_DOSBase,a6
		Move.L	a0,d1
		Move.L	#MODE_NEWFILE,d2
		CALL	Open
		Tst.L	d0
		Beq.S	.saveFileIsNull

			Move.L	d0,-(SP)
			Move.L	d0,a0
			Move.L	head,a2
			Bsr.S	ListNodeSaveToFile

			Move.L	(SP)+,d1
			Move.L	_DOSBase,a6
			CALL	Close
.saveFileIsNull
.headIsNull
	Rts

*******************************************************************************
* In : a0 - char* fileName
* Out : -

LinkedListLoadListFromFile
	Move.L	a0,-(SP)

	Tst.L	head
	Beq.S	.headIsNull

		Move.L	head,a2
		Bsr.S	ListNodeDestructor
.headIsNull
	Move.L	(SP)+,d1
	Move.L	#MODE_OLDFILE,d2
	Move.L	_DOSBase,a6
	CALL	Open
	Move.L	d0,filePointer
	Beq.S	.loadFileIsNull
.whileNotEof
		Lea	aLibraryName,a0
		Lea	anOpenCount,a1
		Move.L	filePointer,a2
		Bsr.S	fscanf
		Move.L	d0,d1
		Cmp.W	#2,d1
		Bne.S	.endOfFileReached

			Tst.L	head
			Beq.S	.headIsNull2

				Move.L	head,a2
				Lea	aLibraryName,a0
				Move.L	anOpenCount,d0
				Bsr.S	ListNodeInsertLibrary
				Bra.S	.headWasNotNull
.headIsNull2
				MoveQ.L	#ListNodeSizeOf,d0
				MoveQ.L	#MEMF_ANY,d1
				Move.L	$4.w,a6
				CALL	AllocMem
				Move.L	d0,head
				Beq.S	.endOfFileReached

				Move.L	d0,a2
				Lea	aLibraryName,a0
				Move.L	anOpenCount,d0
				Bsr.S	ListNodeConstructor

				Tst.L	d0
				Bne.S	.success

				Clr.L	head
.success
.headWasNotNull
	Bra.S	.whileNotEof
.endOfFileReached
	Move.L	_DOSBase,a6
	Move.L	filePointer,d1
	CALL	Close
.loadFileIsNull
	Rts

*******************************************************************************
* In : a0 - char* aLibraryName
*      d0 - unsigned int anOpenCount
*      a2 - ListNode*
* Out : d0 - Nonzero if success, zero if not

ListNodeConstructor
	Clr.L	next(a2)
	Move.L	d0,openCount(a2)
	Move.L	a0,a3
	Bsr.S	strlen
	AddQ.L	#1,d0
	MoveQ.L	#MEMF_ANY,d1
	Move.L	$4.w,a6
	CALL	AllocMem
	Move.L	d0,libraryName(a2)
	Beq.S	.allocFailed
		Move.L	a3,a0
		Move.L	d0,a1
		Bsr.S	strcpy

	MoveQ.L	#1,d0
	Rts
.allocFailed
		Move.L	a2,a1
		MoveQ.L	#ListNodeSizeOf,d0
		CALL	FreeMem

	MoveQ.L	#0,d0
	Rts

*******************************************************************************
* In : a2 - ListNode*

ListNodeDestructor

.killAllNodes
		Move.L	next(a2),a3

		Move.L	libraryName(a2),a0
		Move.L	a0,a1
		Bsr.S	strlen
		AddQ.L	#1,d0
		Move.L	$4.w,a6
		CALL	FreeMem

		Move.L	a2,a1
		MoveQ.L	#ListNodeSizeOf,d0
		CALL	FreeMem

		Move.L	a3,a2

	Cmp.W	#0,a2
	Bne.S	.killAllNodes

	Rts

*******************************************************************************
* In : a0 - char* aLibraryName
*      d0 - unsigned int anOpenCount
*      a2 - ListNode*

ListNodeInsertLibrary

.findLastNode
		Move.L	next(a2),a3
	
		Cmp.W	#0,a3
		Beq.S	.lastNodeFound

		Move.L	a3,a2
	Bra.S	.findLastNode

.lastNodeFound
	Move.L	a0,a3
	Move.L	d0,d2
	MoveQ.L	#ListNodeSizeOf,d0
	MoveQ.L	#MEMF_ANY,d1
	Move.L	$4.w,a6
	CALL	AllocMem
	Move.L	d0,next(a2)
	Beq.S	.nextWasNotNull

		Move.L	a2,a4
		Move.L	d0,a2
		Move.L	a3,a0
		Move.L	d2,d0
		Bsr.S	ListNodeConstructor

		Tst.L	d0
		Bne.S	.success

		Clr.L	next(a4)
.success
.nextWasNotNull
	Rts

*******************************************************************************
* In : a0 - char* libraryName
*      a2 - ListNode*

ListNodeUpdateLibrary

.findNode
	Move.L	a0,a3
	Move.L	libraryName(a2),a1
	Bsr.S	strcmp
	Tst.B	d0
	Bne.S	.stringsNotEqual

		AddQ.L	#1,openCount(a2)
		Bra.S	.return
.stringsNotEqual
		Tst.L	next(a2)
		Beq.S	.nextIsNull

			Move.L	a3,a0
			Move.L	next(a2),a2
			Bra.S	.findNode
.nextIsNull
			MoveQ.L	#ListNodeSizeOf,d0
			MoveQ.L	#MEMF_ANY,d1
			Move.L	$4.w,a6
			CALL	AllocMem
			Move.L	d0,next(a2)
			Beq.S	.return

				Move.L	a2,a4
				Move.L	d0,a2
				Move.L	a3,a0
				MoveQ.L	#1,d0
				Bsr.S	ListNodeConstructor

				Tst.L	d0
				Bne.S	.success

				Clr.L	next(a4)
.success
.return
	Rts

*******************************************************************************
* In : a0 - filepointer
*      a2 - ListNode*

ListNodeSaveToFile
	Move.L	a0,a5
	Move.L	libraryName(a2),a1
	Move.L	openCount(a2),d0
	Bsr.S	fprintf

	Move.L	a5,a0
	Tst.L	next(a2)
	Beq.S	.nextIsNull

		Move.L	next(a2),a2
		Bra.S	ListNodeSaveToFile
.nextIsNull
	Rts

*******************************************************************************
* In : a0 - filepointer
*      a1 - libraryName
*      d0 - openCount
*      a2 - ListNode*

fprintf
	Move.L	a0,d1
	
	Lea	variableArray,a0
	Move.L	a0,d3

	Move.L	a1,(a0)
	Move.L	d0,4(a0)

	Lea	FprintfString,a0
	Move.L	a0,d2
	Move.L	_DOSBase,a6
	CALL	VFPrintf

	Rts

*******************************************************************************
* In : a0 - char* string1
*      a1 - char* string2
* Out : d0 - byte, Zero if equal

strcmp
.whileNotEndOfString
	Move.B	(a0)+,d0
	Move.B	(a1)+,d1

	Tst.B	d0
	Bne.S	.string1NotEnd
		Sub.B	d1,d0
		Bra.S	.return
.string1NotEnd
	Tst.B	d1
	Bne.S	.string2NotEnd
		Sub.B	d1,d0
		Bra.S	.return
.string2NotEnd
	Sub.B	d1,d0
	Beq.S	.whileNotEndOfString
.return
	Rts

*******************************************************************************
* In : a0 - char* source
*      a1 - char* dest

strcpy
.whileNotSourceNull
	Move.B	(a0)+,(a1)+
	Bne.S	.whileNotSourceNull
	Rts
	

*******************************************************************************
* In : a0 - char*
* Out : d0 - int stringlength

strlen
	MoveQ.L	#-1,d0
.whileStringNotNull
		AddQ.L	#1,d0

	Tst.B	(a0)+
	Bne.S	.whileStringNotNull

	Rts

*******************************************************************************
* In : a0 - string buffer
*      a1 - unsigned int pointer
*      a2 - file pointer, _MUST_ not be altered!
* Out : d0 - number of read fields

fscanf
	Move.L	a0,a3
	Move.L	a1,a4

	MoveQ.L	#0,d5                           ; Number of bytes read
.whileNotEof
		Move.L	_DOSBase,a6
		Move.L	a2,d1
		CALL	FGetC
		Tst.L	d0
		Bmi.S	.return0

			Cmp.B	#'\t',d0
			Beq.S	.readUnsignedLong

				AddQ.W	#1,d5
				Cmp.W	#1025,d5
				Beq.S	.return0

				Move.B	d0,(a3)+
	Bra.S	.whileNotEof

.readUnsignedLong
	Clr.B	(a3)
	Lea	aNumberString,a3
	MoveQ.L	#0,d2				; String length
.whileNotEof2
		Move.L	_DOSBase,a6
		Move.L	a2,d1
		CALL	FGetC
		Tst.L	d0
		Bmi.S	.return1

			Cmp.B	#10,d0
			Beq.S	.numberStringRead

				Move.B	d0,(a3)+
				
				AddQ.L	#1,d2
				Cmp.W	#10,d2
				Beq.S	.return1
	Bra.S	.whileNotEof2
.numberStringRead
	SubQ.W	#2,d2
	MoveQ.L	#0,d3
	Lea	aNumberString,a3

	Sub.B	#'0',(a3)
	Add.B	(a3)+,d3
	Tst.W	d2
	Bmi.S	.stringConverted
.convertToInt
		Move.L	d3,d4
		Swap	d4
		Mulu.W	#10,d4
		Swap	d4
		Clr.W	d4
		Mulu.W	#10,d3
		Add.L	d4,d3
		
		Sub.B	#'0',(a3)
		Add.B	(a3)+,d3

	Dbf	d2,.convertToInt
.stringConverted
	Move.L	d3,(a4)

.return2
	MoveQ.L	#2,d0
	Rts

.return0
	MoveQ.L	#0,d0
	Rts

.return1
	MoveQ.L	#1,d0
	Rts

*******************************************************************************
* Struct declarations
*******************************************************************************

		RSRESET
;ListNode
next		Rs.L	1
libraryName	Rs.L	1
openCount	Rs.L	1
ListNodeSizeOf	Rs.B	0

*******************************************************************************
* Data section
*******************************************************************************

		SECTION Datas, Data

DefaultFileName	Dc.B	'S:LibraryOpenCount',0
DosName		Dc.B	'dos.library',0
IllegalLibName	Dc.B	'[Illegal]',0
ReadArgsTempl	Dc.B	'L=LOGFILENAME,I=SAVEINTERVAL/N',0

; fprintf data

FprintfString	Dc.B	'%s\t%lu\n',0

		Dc.B	'$VER: OpenLibraryCounter 1.13 (1997-08-22)',0

*******************************************************************************
* BSS section
*******************************************************************************

		SECTION	BSSs, BSS

; Linked list

head		Ds.L    1
numberOfOpens	Ds.L    1

; Misc variables

_DOSBase	Ds.L	1
myMessagePort	Ds.L	1
anOpenCount	Ds.L	1
filePointer	Ds.L	1
oldOpenLibrary	Ds.L	1

readArgsArray	Ds.L	2
userFileName	Ds.L	1
saveInterval	Ds.L	1

readArgsStruct	Ds.L	1

aLibraryName	Ds.B	1025
aNumberString	Ds.B	10

; fprintf variables

variableArray	Ds.L	2
