/*
 *	Name:				error.c
 *
 * Description:	Error reporting functions for xferq.library
 *
 * Copyright:		1992-1993 by David Jones.
 *
 * Distribution:
 *		This program is free software; you can redistribute it and/or modify
 *		it under the terms of the GNU General Public License as published by
 *		the Free Software Foundation; either version 2 of the License, or
 *		(at your option) any later version.
 *
 *		This program is distributed in the hope that it will be useful,
 *		but WITHOUT ANY WARRANTY; without even the implied warranty of
 *		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *		GNU General Public License for more details.
 *
 *		You should have received a copy of the GNU General Public License
 *		along with this program; if not, write to:
 *
 *				The Free Software Foundation		David Jones
 *				675 Mass Ave							6730 Tooney Drive
 *				Cambridge, MA							Orleans, Ontario
 *				02139										K1C 6R4
 *				USA										Canada
 *
 *	Usenet:	gnu@prep.ai.mit.edu					aa457@freenet.carleton.ca
 *	Fidonet:												1:163/109.8
 *
 *		$Log: $
 *
 */

#include <exec/types.h>
#include <exec/semaphores.h>
#include <proto/exec.h>
#include <proto/utility.h>
#include "xferq.h"
#include "xferqint.h"
#include "xferq_pragmas.h"
#include "execlists.h"


#define	HT_SIZE		71

struct MinList ETable[HT_SIZE];
extern struct SignalSemaphore ObjectLock;

char *ErrorMsgs[] = {
	"Operation successful",
	"Insufficient memory",
	"Disk I/O error",
	"End of queue detected",
	"Required tag missing",
	"Object is locked",
	"Object does not exist",
	"Bad address specification",
	"No session in progress",
	"Illegal use of object",
	"Work already queued"
};


void InitErrors(void)
/*
 *	Does:	Initializes the ENode table upon opening the library.
 */
{
int i;

	for (i = 0; i < HT_SIZE; i++) {
		NEWLIST(&ETable[i]);
	}
}


struct ENode *FindENode(BOOL create)
/*
 *	In:	create			TRUE to create ENode if not found.
 *
 *	Does:	Finds the ENode associated with the current process.  If one
 *			cannot be found, it is created if requested.
 */
{
struct ENode *en;
struct Task *task;
int hash;

	ObtainSemaphore(&ObjectLock);
	task = FindTask(NULL);
	hash = (ULONG)task % HT_SIZE;
	en = FIRST(&ETable[hash]);
	while (NEXT(en)) {
		if (en->task == task) {
			ReleaseSemaphore(&ObjectLock);
			return en;
		}
		en = NEXT(en);
	}
	
	en = NULL;
	if (create) {
		/*
		 *	We must use memobj's low level allocator since AllocObject
		 * calls Error() upon error, which calls FindENode...
		 */
		en = AllocObject(sizeof(struct ENode), XQO_ENODE);
		if (en) {
			en->task = task;
			en->error = XQERROR_OK;
			en->flags = 0;
			ADDHEAD(&ETable[hash], en);
		}
	}
	ReleaseSemaphore(&ObjectLock);
	return en;
}


void DropENode(struct ENode *en)
/*
 *	In:	en					ENode to destroy.
 *
 * Does:	Removes and returns the ENode to the system.  This function
 *			helps keep the ENode table clean.
 */
{

	ObtainSemaphore(&ObjectLock);
	REMOVE(en);
	FreeObject(en);
	ReleaseSemaphore(&ObjectLock);
}


ULONG *PeekError(void)
/*
 *	Does:	Returns a pointer to the address where error codes should be
 *			stored.
 */
{
struct ENode *en;

	en = FindENode(TRUE);
	if (en) {
		return &en->error;
	}
	else {
		return NULL;
	}
}


void Error(long code)
/*
 *	In:	code				Error code to set
 *
 * Does:	Installs the error code in the ENode.
 */
{
BOOL create;
struct ENode *en;

	/*
	 * Prevent recursive loop if ENode can't be created due to lack
	 * of memory.
	 */
	create = (code == XQERROR_NOMEM) ? FALSE : TRUE;
	en = FindENode(create);
	if (en) {
		en->error = code;
	}
}


void SetErrorTags(struct TagItem *tags)
/*
 *	In:	tags				Taglist passed to a tag function
 *
 * Does:	Searches for the tags XQ_ErrorCode and XQ_ErrorMsg and fills
 *			in the data if found.
 */
{
long code, *codeP;
char **msgP;
struct ENode *en;

	/*
	 * Get the error code.  If the ENode cannot be created, then treat
	 * the error as lack of memory.
	 */
	en = FindENode(TRUE);
	if (en) {
		code = en->error;
	}
	else {
		code = XQERROR_NOMEM;
	}
	
	codeP = (long *)GetTagData(XQ_ErrorCode, NULL, tags);
	msgP = (char **)GetTagData(XQ_ErrorMsg, NULL, tags);
	
	/*
	 * Stuff the code and message pointers if they exist.
	 */
	if (codeP) {
		*codeP = code;
	}
	if (msgP) {
		*msgP = XfqErrorMsg(code);
	}
	
	/*
	 * Clear the error code only if any tags were asked for.
	 */
	if ((codeP || msgP) && en) {
		en->error = XQERROR_OK;
	}
}


LONG __saveds __asm LIBGetError(void)
/*
 *	Does:	Returns the error code and clears it.
 */
{
struct ENode *en;
long code;

	en = FindENode(TRUE);
	if (en) {
		code = en->error;
		en->error = XQERROR_OK;
	}
	else {
		code = XQERROR_NOMEM;
	}
	return code;
}


char *__saveds __asm LIBErrorMsg(register __d0 LONG error)
/*
 *	In:	error		D0		Error code returned by XfqGetError
 *
 * Does:	Returns a pointer to an error message associated with the
 *			given error code.
 */
{

	return CopyString(ErrorMsgs[error]);
}



