/*
 * Library Monitor v1.25
 * © Dec 4 1996 Karl J. Ots
 */

/* Include files */
#include	<exec/types.h>
#include	<clib/exec_protos.h>
#include	<clib/dos_protos.h>
#include	<exec/libraries.h>
#include	<exec/execbase.h>
#include	<dos/dos.h>
#include	<dos/rdargs.h>

#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>


/* Functions and Procedures */
void LoadLib(STRPTR LibName, BOOL Report);
void LoadLibs(void);
void FlushLib(STRPTR LibName);
void FlushLibs(void);
void ListLib(STRPTR LibName);
void ListLibs(void);

struct LibInfo *BuildLibInfoList(struct List *LibList);

struct LibInfo *AddLibInfo(struct LibInfo *LibInfo, struct Library *Library);
void KillLibInfo(struct LibInfo *LibInfo);
struct LibInfo *GetLibInfoHead(struct LibInfo *LibInfo);
struct LibInfo *GetLibInfoTail(struct LibInfo *LibInfo);

void OutputHeader(void);
void OutputUsage(void);
void OutputLibInfoHeader(void);
void OutputLibInfo(struct LibInfo *li);
void OutputFlushedLibInfo(struct LibInfo *li);
void OutputLibNotFound(STRPTR name);

void PlainText(void);
void BoldText(void);
void Colour2Text(void);
void Colour3Text(void);


/* Macros and constants */
#define CTRL_C		(SetSignal(NULL,NULL) & SIGBREAKF_CTRL_C)
#define BREAK_TXT	"***Break - LibMon"

#define REPORT		TRUE
#define QUIET		FALSE

#define PROG_DATE		"Dec 4 1996"
#define PROG_VERS		"1.25"


/* Structures and related constants */
#define LA_TEMPLATE	"LIBS=LIBNAMES/M,A=ALL/S,L=LIST/S,F=FLUSH/S"

struct LibArgs {
	STRPTR	*LIBNAMES;
	ULONG	ALL;
	ULONG	LIST;
	ULONG	FLUSH;
	ULONG	Flags;
};

/* LibArgs.Flags bit numbers */
#define LAFB_LIBNAMES	31
#define LAFB_ALL	0
#define LAFB_LIST	1
#define	LAFB_FLUSH	2

/* LibArgs.Flags values */
#define LAFF_NONE	0
#define LAFF_LIBNAMES	(1<<LAFB_LIBNAMES)
#define LAFF_ALL	(1<<LAFB_ALL)
#define LAFF_LIST	(1<<LAFB_LIST)
#define LAFF_FLUSH	(1<<LAFB_FLUSH)

struct LibInfo {
	struct LibInfo *Next;
	struct LibInfo *Prev;
	char	Name[80];
	ULONG	Address;
	UWORD	Version;
	UWORD	Revision;
	BYTE	Priority;
	UWORD	OpenCount;
	BOOL	Flushed;
};


/* Information for C:Version */
const char verstr[] = "$VER: LibMon "PROG_VERS" ("PROG_DATE")";


/* Global Variables */
extern struct ExecBase *SysBase;


/* Start */
void main(void)
{
	struct RDArgs *rdargs = NULL;
	struct LibArgs LibArgs = {NULL,FALSE,FALSE,FALSE,LAFF_NONE};
	int i = 0;
	
	OutputHeader();
	
	/* Check OS version */
	if (SysBase->LibNode.lib_Version < 36)
	{
		puts("Sorry, but this program requires OS2.0 or above");
		exit(0);
	}

	/* Collect arguments */
	rdargs = ReadArgs(LA_TEMPLATE,&LibArgs,NULL);
	if (rdargs == NULL)
	{
		OutputUsage();
		exit(0);
	}
	
	LibArgs.Flags = ((LibArgs.LIBNAMES && 0xFFFFFFFF) << LAFB_LIBNAMES) |
		        ((LibArgs.ALL & 1) << LAFB_ALL) |
	                ((LibArgs.LIST & 1) << LAFB_LIST) |
	                ((LibArgs.FLUSH & 1) << LAFB_FLUSH);
	           
	switch (LibArgs.Flags)
	{
	
	/*-- No library names, ALL flag set --*/
	case (LAFF_ALL):
		LoadLibs();
		break;
	
	/*-- No library names, LIST flag, or ALL and LIST flags set --*/
	case (LAFF_LIST):
	case (LAFF_LIST | LAFF_ALL):
		ListLibs();
		break;
	
	/*-- No library names, FLUSH flag, or ALL and FLUSH flags set --*/
	case (LAFF_FLUSH):
	case (LAFF_FLUSH | LAFF_ALL):
		FlushLibs();
		break;
	
	/*-- Library names, no flags set --*/
	case (LAFF_LIBNAMES):
		for (i=0; LibArgs.LIBNAMES[i] != NULL; i++)
			LoadLib(LibArgs.LIBNAMES[i],REPORT);
		break;
	
	/*-- Library names, LIST flag set --*/
	case (LAFF_LIBNAMES | LAFF_LIST):
		OutputLibInfoHeader();
		for (i=0; LibArgs.LIBNAMES[i] != NULL; i++)
			ListLib(LibArgs.LIBNAMES[i]);
		break;
	
	/*-- Library names, FLUSH flag set --*/
	case (LAFF_LIBNAMES | LAFF_FLUSH):
		for(i=0; LibArgs.LIBNAMES[i] != NULL; i++)
			FlushLib(LibArgs.LIBNAMES[i]);
		break;
	
	/*-- Default --*/
	default:
		OutputUsage();
	}

	FreeArgs(rdargs);
} /* End */

/*--- Processing ---*/

/* Load specified library */
void LoadLib(STRPTR LibName, BOOL Report)
{
	struct Library *Library;

	Library = OldOpenLibrary(LibName);
	if (Library)
	{
		printf("%s v%d.%d loaded.\n",LibName,
		                             Library->lib_Version,
		                             Library->lib_Revision);
		CloseLibrary(Library);
	}
	else if (Report)
		printf("Couldn't open %s.\n",LibName);
}

/* Load all libraries in the LIBS: assignment */
void LoadLibs(void)
{
	BPTR LibsLock;
	struct FileInfoBlock LibsFIB;

	LibsLock = Lock("LIBS:",SHARED_LOCK);
	if (!LibsLock)
		puts("LIBS: is not available!");
	else
	{
		Examine(LibsLock,&LibsFIB);

		while (ExNext(LibsLock,&LibsFIB))
		{
			LoadLib(LibsFIB.fib_FileName,QUIET);
			if (CTRL_C)
			{
				puts(BREAK_TXT);
				break;
			}
		}
		
		UnLock(LibsLock);
	}
}

/* Flush specified library */
void FlushLib(STRPTR LibName)
{
	struct Library *Library;
	struct LibInfo *LibInfo = NULL;

	Forbid();
	Library = (struct Library *)FindName(&SysBase->LibList,LibName);
	if (Library)
	{
		LibInfo = AddLibInfo(NULL,Library);
		if ((BOOL)RemLibrary((struct Library *)LibInfo->Address))
			LibInfo->Flushed = TRUE;
	}
	Permit();
	
	if (LibInfo)
	{
		if (LibInfo->Flushed == TRUE)
			OutputFlushedLibInfo(LibInfo);
		KillLibInfo(LibInfo);
	}
	else
		OutputLibNotFound(LibName);
}

/* Flush all libraries from memory */
void FlushLibs(void)
{
	struct LibInfo *LibInfo;
	struct LibInfo *LibInfoHead;

	Forbid();
	LibInfo = BuildLibInfoList(&SysBase->LibList);
	LibInfoHead = LibInfo;

	while (LibInfo)
	{
		if ((BOOL)RemLibrary((struct Library *)LibInfo->Address))
			LibInfo->Flushed = TRUE;
		LibInfo = LibInfo->Next;
	}
	Permit();

	LibInfo = LibInfoHead;
	while (LibInfo)
	{
		if (LibInfo->Flushed == TRUE)
			OutputFlushedLibInfo(LibInfo);
		LibInfo = LibInfo->Next;
	}
	
	KillLibInfo(LibInfoHead);
}

/* List specified library */
void ListLib(STRPTR LibName)
{
	struct Library *Library;
	struct LibInfo *LibInfo = NULL;

	Forbid();
	Library = (struct Library *)FindName(&SysBase->LibList,LibName);
	if (Library)
		LibInfo = AddLibInfo(NULL,Library);
	Permit();

	if (LibInfo)
	{
		OutputLibInfo(LibInfo);
		KillLibInfo(LibInfo);
	} 
	else
		OutputLibNotFound(LibName);
}

/* List all libraries in memory */
void ListLibs(void)
{
	struct LibInfo *LibInfo;
	struct LibInfo *LibInfoHead;

	Forbid();
	LibInfo = BuildLibInfoList(&SysBase->LibList);
	LibInfoHead = LibInfo;
	Permit();

	OutputLibInfoHeader();
	while (LibInfo)
	{
		OutputLibInfo(LibInfo);
		LibInfo = LibInfo->Next;
		if (CTRL_C)
		{
			puts(BREAK_TXT);
			break;
		}
	}

	KillLibInfo(LibInfoHead);
}

/* Build and return a Library Info list of all libraries in memory */
struct LibInfo *BuildLibInfoList(struct List *LibList)
{
	struct Library *Library;
	struct LibInfo *LibInfo = NULL;

	Library = (struct Library *)LibList->lh_Head;
	while (Library->lib_Node.ln_Succ)
	{
		LibInfo = AddLibInfo(LibInfo,Library);
		Library = (struct Library *)Library->lib_Node.ln_Succ;
	}

	LibInfo = GetLibInfoHead(LibInfo);

	return LibInfo;
}

/*--- LibInfo List Managment ---*/

/* Add specified library data to specified Library Info list */
struct LibInfo *AddLibInfo(struct LibInfo *LibInfo, struct Library *Library)
{
	struct LibInfo *NewLibInfo;

	NewLibInfo = (struct LibInfo *)calloc(sizeof(*NewLibInfo),1);

	strcpy(NewLibInfo->Name,Library->lib_Node.ln_Name);
	NewLibInfo->Address = (ULONG)Library;
	NewLibInfo->Version = Library->lib_Version;
	NewLibInfo->Revision = Library->lib_Revision;
	NewLibInfo->Priority = Library->lib_Node.ln_Pri;
	NewLibInfo->OpenCount = Library->lib_OpenCnt;
	NewLibInfo->Flushed = FALSE;

	if (LibInfo)
	{
		LibInfo = GetLibInfoTail(LibInfo);
		NewLibInfo->Prev = LibInfo;
		LibInfo->Next = NewLibInfo;
	}

	return NewLibInfo;
}

/* Destroy specified Library Information list */
void KillLibInfo(struct LibInfo *LibInfo)
{
	struct LibInfo *NextLibInfo;

	while (LibInfo)
	{
		NextLibInfo = LibInfo->Next;
		free(LibInfo);
		LibInfo = NextLibInfo;
	}
}

/* Get head of specified Library Info list */
struct LibInfo *GetLibInfoHead(struct LibInfo *LibInfo)
{
	while (LibInfo->Prev)
		LibInfo = LibInfo->Prev;

	return LibInfo;
}

/* Get tail of specified Library Info list */
struct LibInfo *GetLibInfoTail(struct LibInfo *LibInfo)
{
	while (LibInfo->Next)
		LibInfo = LibInfo->Next;

	return LibInfo;
}

/*--- Output ---*/

/* Output LibMon header */
void OutputHeader(void)
{
	BoldText();
	printf("Library Monitor v%s © %s Karl J. Ots\n",PROG_VERS,
	                                                  PROG_DATE);
	PlainText();
	puts("");
}

/* Output usage information */
void OutputUsage(void)
{
	puts("Usage: LibMon <libraries> <ALL> <LIST> <FLUSH>");
	puts("");
}

/* Output Library Info Header */
void OutputLibInfoHeader(void)
{
	Colour2Text();
	puts("Address    Library                     Version     Pri   OpenCnt");
	PlainText();
}

/* Output specified Library Info data */
void OutputLibInfo(struct LibInfo *li)
{
	printf("%08X   %-25s   %3d.%-4d   %4d   %5d\n",li->Address,
	                                               li->Name,
	                                               li->Version,
	                                               li->Revision,
	                                               li->Priority,
	                                               li->OpenCount);
}

/* Output flushed Library Information data */
void OutputFlushedLibInfo(struct LibInfo *li)
{
	printf("%s v%d.%d flushed.\n",li->Name,
	                              li->Version,
	                              li->Revision);
}

/* Output that library could not be found */
void OutputLibNotFound(STRPTR name)
{
	printf("           %s was not found in memory.\n",name);
}

/*--- Text colour/style ---*/

/* Change to plain text */
void PlainText(void)
{
	printf("\033[0m");
}

/* Change to bold text */
void BoldText(void)
{
	printf("\033[1m");
}

/* Change text colour to #2 */
void Colour2Text(void)
{
	printf("\033[32m");
}

/* Change text colour to #3 */
void Colour3Text(void)
{
	printf("\033[33m");
}

/*--- Miscellaneous ---*/

/* Disable automatic program abortion if present */
void chkabort(void)
{
	;
}
