/*
 *	Name:				queue.c
 *
 * Description:	Queue management 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: $
 * 1.9  1-May-94
 *  
 * -  Added user flag XQ_IFSENT, if true files queued with both 
 *    XQ_DELETE+XQ_IMMEDIATE will not be deleted if they are not sent.
 *
 */

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

/*
 *		Variables
 */

struct SignalSemaphore QueueLock;
struct Session *QueueList;
BOOL QueueScanned;

extern struct Library *UtilityBase;


void InitQueue(void)
/*
 *	Does:	Initializes the queue manager data structures.
 */
{

	InitSemaphore(&QueueLock);
	QueueList = LibCreateObjectTags(XQO_SESSION,
		XQ_SessFlags, XQSESS_PRIVATE,
		TAG_DONE);
#ifdef DEBUG
	kprintf("QueueList = %08lx\n", QueueList);
#endif
	QueueScanned = FALSE;
}


struct SiteQueue *NewSiteQueue(struct NetAddress *site)
/*
 *	In:	site				Site to create queue for
 *
 * Does:	Allocates a new site queue and links it in to the global
 *			structure.
 */
{
struct SiteQueue *sq;

	sq = AllocObject(sizeof(struct SiteQueue), XQO_SITEQUEUE);
	if (!sq) {
		return NULL;
	}
	sq->sn.addr = LibCopyObject(site);
	NEWLIST(&sq->workList);
	sq->flags = 0;
	sq->numSessions = 0;
	sq->numLocks = 0;
	ADDHEAD(&QueueList->sessList, sq);
	return sq;
}


void DropSiteQueue(struct SiteQueue *sq)
/*
 *	In:	sq					Pointer to site node to drop
 *
 *	Does:	Frees memory for all work nodes queued to site node.
 */
{
struct WorkNode *wn, *twn;

	wn = FIRST(&sq->workList);
	while (twn = NEXT(wn)) {
		LibDropObject(wn);
		wn = twn;
	}
}


void DropQueue(void)
/*
 *	Does:	Frees memory for all queue nodes.
 */
{
struct SiteQueue *sq, *tsq;

	sq = FIRST(&QueueList->sessList);
	while (tsq = NEXT(sq)) {
		REMOVE(sq);
		DropSiteQueue(sq);
		LibDropObject(sq->sn.addr);
		FreeObject(sq);
		sq = tsq;
	}
	/* InitQueue() */
}


struct SiteQueue *FindSiteQueue(struct NetAddress *addr, int actions)
/*
 *	In:	addr				Address to find site queue for
 *			actions			Actions to perform:
 *									FSQ_SCAN		- Scan queue first
 *									FSQ_READ		- Read queue if it exists on disk
 *									FSQ_CREATE	- Create queue if not found
 *
 *	Does:	Finds the site queue corresponding to the supplied address.
 *			The actions flags control the search as shown above.
 *			The function returns a pointer to a site queue, or NULL if error.
 */
{
struct SiteQueue *sq, *tsq;

	if (actions & FSQ_SCAN) {
		if (!QueueScanned) {
			ScanQueue();
			if (!QueueScanned) {
				return NULL;
			}
		}
	}
	sq = FIRST(&QueueList->sessList);
	while (tsq = NEXT(sq)) {
		if (!LibCmpAddressTags(addr, sq->sn.addr,
			XQ_Mandatory, XQADDR_ALLBUTUSER,
			TAG_DONE)) {
			if (actions & FSQ_READ) {
				if (sq->flags & XQSITE_UNREAD) {
					ReadSiteQueue(sq);
					if (sq->flags & XQSITE_UNREAD) {
						return NULL;
					}
				}
			}
			return sq;
		}
		sq = tsq;
	}
	if (actions & FSQ_CREATE) {
		return NewSiteQueue(addr);
	}
	else {
		return NULL;
	}
}


struct SiteQueue *FirstSiteQueue(void *object, struct QueueWalk *qw)
/*
 *	In:	object			Object to walk
 *			qw					Pointer to queue walk structure
 *
 * Does:	Starts a traversal of that portion of the queue database
 *			denoted by object.  The function returns a pointer to the
 *			first site queue, or NULL if not found/error.
 */
{
struct NetAddress *na;

	if (!object) {
		/*
		 *		Walk whole system.
		 *
		 *		A scan is always necessary, since the site queues must
		 *		be in memory for StartWalkSession to bite on anything.
		 */
		if (!QueueScanned) {
			ScanQueue();
			if (!QueueScanned) {
				return NULL;
			}
		}
		object = QueueList;
	}
	if (ObjectType(object) == XQO_SESSION) {
		na = StartWalkSession(object, &qw->sn);
		return FindSiteQueue(na, qw->actions);
	}
	else {
		qw->sn = NULL;
		return FindSiteQueue(object, qw->actions);
	}
}


struct SiteQueue *NextSiteQueue(struct QueueWalk *qw)
/*
 *	In:	qw					Pointer to queue walk structure
 *
 * Does:	Continues the traversal of the queue database.  Returns
 *			are as for FirstSiteQueue().
 */
{
struct NetAddress *na;

	if (qw->sn) {
		na = NextWalkSession(&qw->sn);
		if (na) {
			return FindSiteQueue(na, qw->actions);
		}
	}
	return NULL;
}


int EligibleWork(void *object, struct WorkNode **pwn,
	struct TagItem *tags)
/*
 *	In:	object			Object to find eligible work for.
 *			actions			Actions for FindSiteQueue().
 *			pwn				Pointer for work node info.
 *			tags				Taglist controlling scope of search.
 *
 * Does:	Finds work that can be sent.  If work is found, and wn
 *			is not NULL, it will be written back with the work.
 *			The function returns the value for AnyWork().
 */
{
struct QueueWalk qw;
int minPri, maxPri, status;
struct SiteQueue *sq;
struct WorkNode *wn, *twn;

	minPri = GetTagData(XQ_MinPri, XQ_MINPRI, tags);
	maxPri = GetTagData(XQ_MaxPri, XQ_MAXPRI, tags);
	
	qw.actions = FSQ_SCAN | FSQ_READ;
	status = NO_WORK;
	
	sq = FirstSiteQueue(object, &qw);
	while (sq) {
		if (sq->numLocks && status == NO_WORK) {
			status = PENDING_WORK;
		}
		wn = FIRST(&sq->workList);
		while (twn = NEXT(wn)) {
			if (wn->status == XQ_UNSENT && !(wn->userFlags & XQ_SENDLATER) &&
				wn->node.ln_Pri >= minPri && wn->node.ln_Pri <= maxPri) {
				
				if (wn->sysFlags & XQ_LOCKED) {
					status = LOCKED_WORK;
				}
				else {
					if (pwn) {
						*pwn = wn;
					}
					return UNLOCKED_WORK;
				}
			}
			wn = twn;
		}
		sq = NextSiteQueue(&qw);
	}
	if (pwn) {
		*pwn = NULL;
	}
	return status;
}


BOOL AnyDirty(void)
/*
 *	Does:	Returns TRUE if any of the queues are dirty, FALSE otherwise.
 */
{
struct SiteQueue *sq, *tsq;

	sq = FIRST(&QueueList->sessList);
	while (tsq = NEXT(sq)) {
		if (sq->flags & XQSITE_DIRTY) {
			return TRUE;
		}
		sq = tsq;
	}
	return FALSE;
}


struct WorkNode *LockWork(struct WorkNode *wn)
/*
 *	In:	wn					Pointer to node to lock.
 *
 *	Does:	If node is locked, then set error code and return NULL.
 *			Otherwise assert lock bit and return pointer to node.
 */
{

	if (wn->sysFlags & XQ_LOCKED) {
		XfqSetLocalError(XQERROR_LOCKED);
		return NULL;
	}
	else {
		wn->sysFlags |= XQ_LOCKED;
		return wn;
	}
}


void LibUnlockWork(struct WorkNode *wn)
/*
 *	In:	wn			A0		Pointer to node to unlock
 *
 *	Does:	Clears the lock bit in the work node.
 */
{

	ObtainSemaphore(&QueueLock);
	wn->sysFlags &= ~XQ_LOCKED;
	ReleaseSemaphore(&QueueLock);
}


void __saveds __asm LIBUnlockWork(register __a0 struct WorkNode *wn)
{

	LibUnlockWork(wn);
	SetErrorTags(NULL);
}


ULONG LibAnyWork(void *object,
	struct TagItem *tags)
/*
 *	In:	object	A0		Pointer to object to check work for
 *			tags		A1		Taglist giving priority window
 *
 *	Does:	Checks to see if any work is queued to the object in question.
 *			The function returns one of the following:
 *
 *			NO_WORK			No work is queued up
 *			LOCKED_WORK		Work is queued, but other users have locked it
 *			UNLOCKED_WORK	Work is queued and unlocked
 *
 *			The object passed in may be a session or an address.  If it's
 *			a session, the function checks every address in the session
 *			for work.
 */
{
ULONG result;

	ObtainSemaphore(&QueueLock);
	result = EligibleWork(object, NULL, tags);
	ReleaseSemaphore(&QueueLock);
	return result;
}


ULONG __saveds __asm LIBAnyWork(register __a0 void *object,
	register __a1 struct TagItem *tags)
{
ULONG result;

	result = LibAnyWork(object, tags);
	SetErrorTags(tags);
	return result;
}


struct WorkNode *SimpleFindWork(void *object, char *name)
/*
 *	In:	object			Address/session/queue to search in
 *			name				Name of node to find
 *
 * Does:	Searches for the work in the given area(s) and returns it
 *			if found.
 */
{
struct QueueWalk qw;
struct SiteQueue *sq;
struct WorkNode *wn;

	qw.actions = FSQ_SCAN | FSQ_READ;
	sq = FirstSiteQueue(object, &qw);
	while (sq) {
		wn = (struct WorkNode *)FindNameNoCase(&sq->workList, name);
		if (wn) {
			return wn;
		}
		sq = NextSiteQueue(&qw);
	}
	return NULL;
}


struct WorkNode *LibFindWork(
	struct TagItem *tags)
/*
 *	In:	tags		A0		Pointer to tag list
 *
 *	Does:	Searches the queue for work satisfying the conditions set by
 *			the tag list.  If a work node is found, its lock bit is set
 *			and its address is returned.  Otherwise, the function returns
 *			NULL.  Valid tags are:
 *
 *			XQ_Name		Fully-pathed filename of file (mandatory)
 *			XQ_Site		Site that file was sent to (optional)
 *
 *			If XQ_Site is not given, all sites in the queue are searched.
 *			If the file is being sent to more than one site, you'll never
 *			find out.
 *
 *			If XQ_Site is given, it may be either an address or a session.
 *			If a session, all sites on the session are searched.
 */
{
void *site;
char *name;
struct WorkNode *wn;

	name = (char *)GetTagData(XQ_Name, NULL, tags);
	if (!name) {
		XfqSetLocalError(XQERROR_NOTAG);
		return NULL;
	}
	
	site = (void *)GetTagData(XQ_Site, NULL, tags);
	
	/*
	 *	The name is stored fully pathed so it must be searched fully
	 * pathed.
	 */
	name = FullPath(name);
	if (!name) {
		return NULL;
	}

	ObtainSemaphore(&QueueLock);
	wn = SimpleFindWork(site, name);
	if (wn) {
		wn = LockWork(wn);
	}
	else {
		XfqSetLocalError(XQERROR_NOEXIST);
		wn = NULL;
	}
	ReleaseSemaphore(&QueueLock);
	LibDropObject(name);
	
	return wn;
}


struct WorkNode *__saveds __asm LIBFindWork(
	register __a0 struct TagItem *tags)
{
struct WorkNode *wn;

	wn = LibFindWork(tags);
	SetErrorTags(tags);
	return wn;
}


struct WorkNode *LibGetWork(void *object,
	struct TagItem *tags)
/*
 *	In:	object	A0		Pointer to address or session
 *			tags		A1		Taglist giving priority window
 *
 *	Does:	Finds the next item of work that can be sent, locks it and
 *			returns it.  If object is an address, then only that queue
 *			is searched.  If object is a session, then all addresses in
 *			the session are searched.
 *
 *			Criteria for transmission include:
 *			-	Work must not be locked.
 *			-	Work must be UNSENT.
 *			-	Work must be within priority bounds of taglist
 *			-	Work must be higher priority than other eligible work
 *			-	Work must not be SENDLATER
 */
{
struct WorkNode *wn;

	ObtainSemaphore(&QueueLock);
	EligibleWork(object, &wn, tags);
	if (wn) {
		LockWork(wn);
	}
	else {
		XfqSetLocalError(XQERROR_NOEXIST);
	}
	ReleaseSemaphore(&QueueLock);
	return wn;
}


struct WorkNode *__saveds __asm LIBGetWork(register __a0 void *object,
	register __a1 struct TagItem *tags)
{
struct WorkNode *wn;

	wn = LibGetWork(object, tags);
	SetErrorTags(tags);
	return wn;
}


ULONG LibRemoveWork(struct WorkNode *wn)
/*
 *	In:	wn			A0		Pointer to node to remove
 *
 *	Does:	Removes the node from its queue so that NO queue management
 *			function can see it.  XfqAddWork() can be used to re-insert
 *			the node.
 *
 *			If the file has been sent and deletion or truncation has been
 *			specified, then kiss the file good-bye.
 */
{
ULONG result;

	ObtainSemaphore(&QueueLock);
	if (wn->status == XQ_SENT) {
		if (wn->userFlags & XQ_DELETE) {
			DeleteFile(wn->node.ln_Name);
			if (IoErr()) {
				XfqSetLocalError(XQERROR_DOS);
				result = XQRM_ERROR;
			}
			else {
				result = XQRM_DELETED;
			}
		}
		else if (wn->userFlags & XQ_TRUNCATE) {
			result = TruncateFile(wn->node.ln_Name);
		}
		else {
			result = XQRM_SENT;
		}
	}
	else {
		result = XQRM_UNSENT;
	}
	REMOVE(wn);
	wn->site->flags |= XQSITE_DIRTY;
	wn->sysFlags &= ~XQ_INQUEUE;
	ReleaseSemaphore(&QueueLock);
	return result;
}


ULONG __saveds __asm LIBRemoveWork(register __a0 struct WorkNode *wn)
{
ULONG result;

	result = LibRemoveWork(wn);
	SetErrorTags(NULL);
	return result;
}


LONG LibMaxSitePri(void *object)
/*
 *	In:	object	A0		Pointer to address or session
 *
 *	Does:	Determines the maximum priority of any work described by
 *			the supplied object.  Object may be either a site or a session.
 */
{
struct WorkNode *wn;
LONG result;

	ObtainSemaphore(&QueueLock);
	EligibleWork(object, &wn, NULL);
	if (wn) {
		result = wn->node.ln_Pri;
	}
	else {
		result = XQ_NOPRI;
	}
	ReleaseSemaphore(&QueueLock);
	return result;
}


LONG __saveds __asm LIBMaxSitePri(register __a0 void *object)
{
LONG result;

	result = LibMaxSitePri(object);
	SetErrorTags(NULL);
	return result;
}


void LibWalkQueueCallBack(
	struct NetAddress *addr,
	struct TagItem *tags,
	void *globals)
/*
 *	In:	addr		A0		Pointer to address to walk through
 *			tags		A1		Pointer to tag list
 *			globals	A4		Pointer to caller's global area
 *
 *	Does:	Calls a user-supplied hook function for each entry in the
 *			queue.  The function must return TRUE to continue the walk,
 *			FALSE otherwise.  Valid tags are:
 *
 *			XQ_Reverse		Set to TRUE to walk from low-to-high priority
 *			XQ_CallBack		Data is pointer to hook structure.
 *
 *	Note:	Access to all queues in the system is disabled while this
 *			function executes.  Be quick!
 */
{
struct SiteQueue *sq;
struct WorkNode *wn, *twn;
struct Hook *hook;
BOOL reverse, incLocked;

	hook = (struct Hook *)GetTagData(XQ_CallBack, NULL, tags);
	if (!hook) {
		XfqSetLocalError(XQERROR_NOTAG);
		return;
	}
	reverse = (BOOL)GetTagData(XQ_Reverse, FALSE, tags);
	incLocked = (BOOL)GetTagData(XQ_IncLocked, FALSE, tags);
	ObtainSemaphore(&QueueLock);
	sq = FindSiteQueue(addr, FSQ_READ | FSQ_SCAN);
	if (!sq) {
		XfqSetLocalError(XQERROR_NOEXIST);
		return;
	}
	if (reverse) {
		wn = LAST(&sq->workList);
		while (twn = PREV(wn)) {
			if (incLocked || !(wn->sysFlags & XQ_LOCKED)) {
				if (!CallHookRes(hook, wn, tags, globals)) {
					break;
				}
			}
			wn = twn;
		}
	}
	else {
		wn = FIRST(&sq->workList);
		while (twn = NEXT(wn)) {
			if (incLocked || !(wn->sysFlags & XQ_LOCKED)) {
				if (!CallHookRes(hook, wn, tags, globals)) {
					break;
				}
			}
			wn = twn;
		}
	}
	ReleaseSemaphore(&QueueLock);
}


void __saveds __asm LIBWalkQueueCallBack(register __a0 struct NetAddress *na,
	register __a1 struct TagItem *tags, register __a4 void *globals)
{

	LibWalkQueueCallBack(na, tags, globals);
	SetErrorTags(tags);
}


ULONG SimpleAddWork(struct SiteQueue *sq, struct WorkNode *wn)
/*
 *	In:	sq					Site queue to add to
 *			wn					Work node to add
 *
 * Does:	Performs most of the work required for XfqAddWork().  This
 *			function is also called from ReadQueue().
 */
{

	if (!sq->numSessions) {
		/*
		 *	If IMMEDIATE and no session is up, then don't add work.
		 */
		if (wn->userFlags & XQ_IMMEDIATE) {
			XfqSetLocalError(XQERROR_NOSESSION);
			return FALSE;
		}
		/*
		 * If SENDLATER and no session is up, then clear the SENDLATER bit.
		 */
		if (wn->userFlags & XQ_SENDLATER) {
			wn->userFlags &= ~XQ_SENDLATER;
		}
	}
	/*
	 *	Give the work a unique sequence number for error recovery.
	 *	Once xobject is in place, this should be done by the modify
	 *	method.
	 */
	if (!wn->seq) {
		wn->seq = LibNextSeq(1);
		if (!wn->seq) {
			return FALSE;
		}
	}
	ENQUEUE(&sq->workList, wn);
	/*
	 *	Fix up links to address, SiteQueue.
	 * We don't mark queue as dirty here, since queue reading also
	 * calls this function.
	 */
	wn->site = sq;
	if (wn->addr) {
		LibDropObject(wn->addr);
	}
	wn->addr = LibCopyObject(sq->sn.addr);
	wn->sysFlags |= XQ_INQUEUE;
	return TRUE;
}


ULONG LibAddWork(struct NetAddress *addr,
	struct WorkNode *wn)
/*
 *	In:	addr		A0		Address to add work to
 *			wn			A1		Work node to add
 *
 * Does:	Enqueues the work onto the given queue.
 *
 */
{
struct SiteQueue *sq;
ULONG result;

	ObtainSemaphore(&QueueLock);
	/*
	 *	Find place to add work.
	 */
	sq = FindSiteQueue(addr, FSQ_SCAN | FSQ_READ | FSQ_CREATE);
	if (!sq) {
		ReleaseSemaphore(&QueueLock);
		return FALSE;
	}
	/*
	 * Check to see if work is already in the queue.  This is the
	 * only reliable way to perform this check.
	 */
	if (SimpleFindWork(addr, wn->node.ln_Name)) {
		XfqSetLocalError(XQERROR_QUEUED);
		ReleaseSemaphore(&QueueLock);
		return FALSE;
	}
	
	result = SimpleAddWork(sq, wn);
	
	if (result) {
		LockWork(wn);
	}
	sq->flags |= XQSITE_DIRTY;
	
	ReleaseSemaphore(&QueueLock);
	return result;
}


ULONG __saveds __asm LIBAddWork(register __a0 struct NetAddress *na,
	register __a1 struct WorkNode *wn)
{
ULONG result;

	result = LibAddWork(na, wn);
	SetErrorTags(NULL);
	return result;
}


BOOL PreExamWork(struct WorkNode *wn)
/*
 *	In:	wn					Work node to examine.
 *
 * Does:	Locks the queue for data integrity.
 */
{

	ObtainSemaphore(&QueueLock);
	return TRUE;
}

BOOL PostExamWork(struct WorkNode *wn)
/*
 *	In:	wn					Work node to examine.
 *
 * Does:	Releases lock after examining node.
 */
{

	ReleaseSemaphore(&QueueLock);
	return TRUE;
}


BOOL PreModifyWork(struct WorkNode *wn)
/*
 *	In:	wn					Work node to modify
 *
 *	Does:	This function is called before a work node is modified.
 *			If the work node was in the queue, then it must be removed,
 *			since the new node will not be at the same location.
 */
{

	ObtainSemaphore(&QueueLock);
	/*
	 *	If the node is in the queue, then remove it.
	 */
	if (wn->sysFlags & XQ_INQUEUE) {
		REMOVE(wn);
	}
	return TRUE;
}


BOOL PostModifyWork(struct WorkNode *wn)
/*
 *	In:	wn					Pointer to modified work
 *
 * Does:	This function is called after a work node is modified.
 *			
 *			If the node was in the queue, then the priority/flags
 *			may have changed.  This may result in a change in position
 *			on the queue, or a "no session" error.
 */
{
char *name;

	/*
	 *	Compute full path of name
	 */
	name = wn->node.ln_Name;
	wn->node.ln_Name = FullPath(name);
	LibDropObject(name);
	if (!wn->node.ln_Name) {
		ReleaseSemaphore(&QueueLock);
		return FALSE;
	}
	/*
	 *	If the node was in the queue at the time the modification started,
	 * then re-insert into the queue.
	 */
	if (wn->sysFlags & XQ_INQUEUE) {
		LibAddWork(wn->addr, wn);
	}
	ReleaseSemaphore(&QueueLock);
	return TRUE;
}


struct Session *LibGetSiteList(void)
/*
 *	Does:	Creates a private session whose addresses are those that have
 *			work queued to them.
 */
{
struct QueueWalk qw;
struct Session *session;
struct SiteQueue *sq;

	session = LibCreateObjectTags(XQO_SESSION,
		XQ_SessFlags, XQSESS_PRIVATE,
		TAG_DONE);
	if (!session) {
		return NULL;
	}
	ObtainSemaphore(&QueueLock);
	qw.actions = FSQ_SCAN | FSQ_READ;
	sq = FirstSiteQueue(NULL, &qw);
	while (sq) {
		if (NEXT(FIRST(&sq->workList))) {
			if (!LibBeginSessionTags(session,
				XQ_AddTail, sq->sn.addr,
				TAG_DONE)) {
				
				LibDropObject(session);
				ReleaseSemaphore(&QueueLock);
				return NULL;
			}
		}
		sq = NextSiteQueue(&qw);
	}
	ReleaseSemaphore(&QueueLock);
	return session;
}


struct Session *__saveds __asm LIBGetSiteList(void)
{
struct Session *sess;

	sess = LibGetSiteList();
	SetErrorTags(NULL);
	return sess;
}


ULONG LibAddWorkQuick(char *site,
	char *name, char *asname,
	LONG pri, LONG flags)
/*
 *	In:	site		A0		Address STRING to add work to
 *			name		A1		Name of file to add
 *			asname	A2		Name to send file as
 *			pri		D0		Priority to send file at
 *			flags		D1		Flags controlling file handling
 *
 *	Does:	This is a quick-and-dirty addition function.  It parses the
 *			address string, creates a work node and adds it to the queue.
 *			Function returns TRUE if it worked.
 */
{
struct NetAddress *addr;
struct WorkNode *wn;

	addr = LibGetAddressTags(site, NULL,
		XQ_Mandatory, XQADDR_2D,
		XQ_Optional, XQADDR_ANYTHING,
		TAG_DONE);
#ifdef DEBUG
	kprintf("AddWorkQuick address = %08lx\n", addr);
#endif
	if (!addr) {
		return FALSE;
	}
	
	/* Create the work node */
	wn = LibCreateObjectTags(XQO_WORKNODE,
		XQ_Name, name,
		XQ_AsName, asname,
		XQ_Pri, pri,
		XQ_Flags, flags,
		TAG_DONE);
	if (!wn) {
		LibDropObject(addr);
		return FALSE;
	}
	
	/* Add the work node */
	if (!LibAddWork(addr, wn)) {
		LibDropObject(wn);
		LibDropObject(addr);
		return FALSE;
	}
	LibUnlockWork(wn);
	
	/* The address is no longer needed */
	LibDropObject(addr);
	return TRUE;
}


ULONG __saveds __asm LIBAddWorkQuick(register __a0 char *addr,
	register __a1 char *name, register __a2 char *asname,
	register __d0 LONG pri, register __d1 LONG flags)
{
ULONG result;

	result = LibAddWorkQuick(addr, name, asname, pri, flags);
	SetErrorTags(NULL);
	return result;
}


void SessionDownQueue(struct SiteQueue *sq)
/*
 *	In:	sq					Site node to scan for dead work
 *
 * Does:	All sessions just went down, so remove all IMMEDIATE nodes and
 *			clear the SENDLATER bits.
 */
{
struct WorkNode *wn, *twn;

	wn = FIRST(&sq->workList);
	while (twn = NEXT(wn)) {
		wn->userFlags &= ~XQ_SENDLATER;
		if (wn->userFlags & XQ_IMMEDIATE) {
/**/
      if (wn->userFlags & XQ_IFSENT)
        wn->status = XQ_UNSENT;
      else
/**/
  			wn->status = XQ_SENT;

			LibRemoveWork(wn);
			LibDropObject(wn);
		}
		wn = twn;
	}
}


ULONG LibHoldMailer(struct NetAddress *addr)
/*
 *	In:	addr		A0		Address to lock mailer for.
 *
 * Does:	Puts a mailer on hold by setting a flag that affects the result of
 *			XfqAnyWork().  This function is used to delay a mailer session
 *			while work is being created.
 */
{
struct SiteQueue *sq;

	ObtainSemaphore(&QueueLock);
	sq = FindSiteQueue(addr, FSQ_CREATE);
	if (sq) {
		sq->numLocks++;
	}
	ReleaseSemaphore(&QueueLock);
	return (sq) ? (ULONG)TRUE : (ULONG)FALSE;
}


ULONG __saveds __asm LIBHoldMailer(register __a0 struct NetAddress *na)
{
ULONG result;

	result = LibHoldMailer(na);
	SetErrorTags(NULL);
	return result;
}


ULONG LibReleaseMailer(struct NetAddress *addr)
/*
 *	In:	addr		A0		Address to release mailer for.
 *
 * Does:	Releases a hold requested by XfqHoldMailer().
 */
{
struct SiteQueue *sq;

	ObtainSemaphore(&QueueLock);
	sq = FindSiteQueue(addr, FSQ_CREATE);
	if (sq && sq->numLocks > 0) {
		sq->numLocks--;
	}
	ReleaseSemaphore(&QueueLock);
	SetErrorTags(NULL);
	return (sq) ? (ULONG)TRUE : (ULONG)FALSE;
}


ULONG __saveds __asm LIBReleaseMailer(register __a0 struct NetAddress *na)
{
ULONG result;

	result = LibReleaseMailer(na);
	SetErrorTags(NULL);
	return result;
}



