/*
 *	Network Queueing System (NQS)
 *  This version of NQS is Copyright (C) 1992  John Roman
 *
 *  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 1, 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
/*
*  PROJECT:     Network Queueing System
*  AUTHOR:      John Roman
*
*  Modification history:
*
*       Version Who     When            Description
*       -------+-------+---------------+-------------------------
*       V01.10  JRR                     Initial version.
*       V01.20  JRR     16-Jan-1992	Added support for RS6000.
*       V01.3   JRR     12-Feb-1992	Fixed static declaration of routines.
*       V01.4   JRR     12-Feb-1992	Fixed problem in above.
*       V01.5   JRR     08-Apr-1992     Added CERN enhancments.
*	V01.6	JRR	17-Jun-1992	Added header.
*	V01.7	JRR	10-Nov-1992	Added support for HPUX.
*	V01.8	JRR	08-Mar-1993	Added Boeing enhancement for files.
*	V01.9	JRR	01-Mar-1994	Added support for SOLARIS.
*/
/*++ nqs_quereq.c - Network Queueing System
 *
 * $Source: /usr2/jrroma/nqs/nqs-3.35.6/src/RCS/nqs_quereq.c,v $
 *
 * DESCRIPTION:
 *
 *	Queue an NQS request for execution.
 *
 *
 *	Author:
 *	-------
 *	Brent A. Kingsbury, Sterling Software Incorporated.
 *	August 12, 1985.
 *
 *
 * STANDARDS VIOLATIONS:
 *   None.
 *
 * REVISION HISTORY: ($Revision: 1.9 $ $Date: 1994/03/30 20:36:47 $ $State: Exp $)
 * $Log: nqs_quereq.c,v $
 * Revision 1.9  1994/03/30  20:36:47  jrroma
 * Version 3.35.6
 *
 * Revision 1.8  93/07/13  21:33:56  jrroma
 * Version 3.34
 * 
 * Revision 1.7  92/12/22  15:41:27  jrroma
 * Version 3.30
 * 
 * Revision 1.6  92/06/18  17:31:15  jrroma
 * Added gnu header
 * 
 * Revision 1.5  92/05/06  10:42:26  jrroma
 *  Version 3.20
 * 
 * Revision 1.4  92/02/12  16:13:53  jrroma
 * *** empty log message ***
 * 
 * Revision 1.3  92/02/12  13:48:05  jrroma
 * F
 * Fixed static declaration of routines.
 * 
 * Revision 1.2  92/01/17  11:05:55  jrroma
 * Added support for RS6000.
 * 
 * Revision 1.1  92/01/17  11:05:06  jrroma
 * Initial revision
 * 
 *
 */

#include "nqs.h"			/* NQS constants and data types */
#include <string.h>
#include "informcc.h"			/* NQS information completion */
					/* codes and masks */
#include "transactcc.h"			/* NQS transaction completion codes */
#include "nqsxvars.h"			/* NQS global vars and directories */


#ifndef __CEXTRACT__
#if __STDC__

static int flag_upd ( struct request *req, short setflags, short clflags );
static int resolvepath ( struct rawreq *rawreq, Mid_t *mid_resolve, char *path_resolve, short access_mode, int length, char suffixchar );
static void subs_seqno ( char *cp, int seqno );

#else /* __STDC__ */

static int flag_upd (/* struct request *req, short setflags, short clflags */);
static int resolvepath (/* struct rawreq *rawreq, Mid_t *mid_resolve, char *path_resolve, short access_mode, int length, char suffixchar */);
static void subs_seqno (/* char *cp, int seqno */);

#endif /* __STDC__ */
#endif /* __CEXTRACT__ */

/*** nqs_quereq
 *
 *
 *	long nqs_quereq():
 *	Queue an NQS request for execution.
 *
 *	Returns:
 *		TCML_SUBMITTED: if successful in queueing the request;
 *		otherwise, a failure transaction code is returned (TCMx_).
 *		The information completion code portion of the return
 *		value is 0.
 */
long nqs_quereq (mode, ctrlname, orig_seqno, orig_mid, destqueue, frommid)
int mode;				/* Queueing mode:  0=new; 1=local */
					/* pipe queue; 2=remote pipe queue */
char *ctrlname;				/* The name of the req control file */
					/* (Only meaningful if mode = 0) */
long orig_seqno;			/* Original request sequence# */
					/* (Only meaningful if mode > 0) */
Mid_t orig_mid;				/* Original request machine-id */
					/* (Only meaningful if mode > 0) */
char *destqueue;			/* Destination queue */
					/* (Only meaningful if mode > 0) */
Mid_t frommid;				/* Machine delivering request */
{					/* (Only meaningful if mode = 2) */

	char newpath [MAX_PATHNAME+1];	/* New pathname */
	char oldpath [MAX_PATHNAME+1];	/* Old pathname */
	struct rawreq rawreq;		/* Raw request structure */
	int cfd;			/* Control file file-descriptor */
	long insqueres;			/* nsq_enque() result code */
	register int i;			/* Counter */
        char *requests_dir;             /* Fully qualified file name */
	char *cp;			/* Utility character pointer */

	if (Debug > 2) {
		printf ("D$nqs_quereq: Entering with seqno %d.\n", orig_seqno);
		fflush (stdout);
	}
	if (mode == 0) {
		/*
		 *  This request is a brand new request, with its
		 *  associated files in the new local request
		 *  directory.
		 */
                requests_dir = getfilnam (Nqs_requests, SPOOLDIR);
                if (requests_dir == (char *)NULL) return (TCML_INTERNERR);
                sprintf (newpath, "%s/%s", requests_dir, ctrlname);
                relfilnam (requests_dir);

	}
	else {
		/*
		 *  The request is being routed by a pipe
		 *  queue.
		 */
		pack6name (newpath, Nqs_control,
			  (int) (orig_seqno % MAX_CTRLSUBDIRS), (char *) 0,
			  (long) orig_seqno, 5, (long) orig_mid, 6, 0, 0);
	}
	if ((cfd = open (newpath, O_RDWR)) == -1) {
		/*
		 *  We are unable to open the control file!
		 */
		if (nqs_enfile ()) {
			printf ("I$Unable to accept new request because of ");
			printf ("file descriptor shortage.\n");
			fflush (stdout);
			return (TCML_ENFILE);
		}
		return (TCML_UNAFAILURE);
	}
	if (readreq (cfd, &rawreq) == -1) {
		/*
		 *  Request is corrupt.
		 */
		if (mode == 0) {
			rawreq.tcm = TCML_UNAFAILURE;
			writereq (cfd, &rawreq);
		}
		close (cfd);			/* Close request file */
		return (TCML_UNAFAILURE);	/* Failure */
	}
	/*
	 *  Assign sequence number to request.
	 */
	if (mode == 0) {
		/*
		 *  The request is being submitted for the first time and
		 *  does not have a sequence number or transaction-id
		 *  assigned to it.
		 */
		rawreq.trans_id = 0;		/* No arguments about this */
		time (&rawreq.create_time);
		rawreq.tcm = TCML_SUBMITTED;	/* Might overwrite this below */
		rawreq.orig_seqno = Seqno_user;	/* Seq# the request will have */
	}					/* if it queues successfully */
	else {
		/*
		 *  The request is being routed by a pipe queue.
		 *  Copy the destination queue name into the queue
		 *  field of the rawreq structure for the request.
		 */
		strcpy (rawreq.quename, destqueue);
		if (mode == 2) {
			/*
			 *  Set values out of paranoia (that should
			 *  already be set) for requests that are
			 *  being queued from a remote machine.
			 */
			rawreq.trans_id = 0;	/* No transaction-id */
			rawreq.flags |= RQF_EXTERNAL;
		}
	}
	/*
	 *  Assign queue entry time to request.
	 */
	time (&rawreq.enter_time);
	/*
	 *  Manufacture default output file pathnames for batch request
	 *  stderr, stdlog, and stdout files as necessary.
	 */
	if (rawreq.type == RTYPE_BATCH) {
		i = strlen (rawreq.v.bat.stderr_name);
		if ((rawreq.v.bat.stderr_acc & OMD_EO) == 0 &&
		    (i == 0 || rawreq.v.bat.stderr_name [i-1] == '/')) {
			/*
			 *  The stderr file name needs to be resolved.
			 *  The user did NOT specify the pathname for
			 *  the stderr file, and the stderr file is to
			 *  be placed on the execution machine, or
			 *  returned to the submitting machine as
			 *  determined by the OMD_M_KEEP modifier bit.
			 */
			if (Debug > 1) {
				printf ("D$Stderr name needs resolution.\n");
				fflush (stdout);
			}
			if (resolvepath (&rawreq, &rawreq.v.bat.stderr_mid,
					 rawreq.v.bat.stderr_name,
					 rawreq.v.bat.stderr_acc, i,
					 'e') == -1) {
				/*
				 *  The resolved pathname will be too long.
				 */
				if (mode == 0) {
					rawreq.tcm = TCML_PATHLEN;
					writereq (cfd, &rawreq);
				}
				close (cfd);		/* Close request file */
				return (TCML_PATHLEN);
			}
			if (Debug > 1) {
				printf ("D$Resolved stderr name = %s.\n",
					 rawreq.v.bat.stderr_name);
				fflush (stdout);
			}
		} else {
		    /*
		     * Does the name include a hash token indicating that
		     * the request number should be substituted?
		     */
		    cp = strrchr (rawreq.v.bat.stderr_name,  '/');
		    if (cp == NULL) cp = rawreq.v.bat.stderr_name;
		    else cp++;
		    if (strchr( cp, SEQNO_TOKEN) ) {
			subs_seqno (rawreq.v.bat.stderr_name, rawreq.orig_seqno);
		    }
		}
		i = strlen (rawreq.v.bat.stdlog_name);
		if (i == 0 || rawreq.v.bat.stdlog_name [i-1] == '/') {
			/*
			 *  The stdlog file name needs to be resolved.
			 *  The user did NOT specify the pathname for
			 *  the stdlog file, and the stdlog file is to
			 *  be placed on the execution machine, or
			 *  returned to the submitting machine as
			 *  determined by the OMD_M_KEEP modifier bit.
			 */
			if (Debug > 1) {
				printf ("D$Stdlog name needs resolution.\n");
				fflush (stdout);
			}
			if (resolvepath (&rawreq, &rawreq.v.bat.stdlog_mid,
					 rawreq.v.bat.stdlog_name,
					 rawreq.v.bat.stdlog_acc, i,
					 'l') == -1) {
				/*
				 *  The resolved pathname will be too long.
				 */
				if (mode == 0) {
					rawreq.tcm = TCML_PATHLEN;
					writereq (cfd, &rawreq);
				}
				close (cfd);		/* Close request file */
				return (TCML_PATHLEN);
			}
			if (Debug > 1) {
				printf ("D$Resolved stdlog name = %s.\n",
					 rawreq.v.bat.stdlog_name);
				fflush (stdout);
			}
		} else {
		    /*
		     * Does the name include a hash token indicating that
		     * the request number should be substituted?
		     */
		    cp = strrchr (rawreq.v.bat.stdlog_name,  '/');
		    if (cp == NULL) cp = rawreq.v.bat.stdlog_name;
		    else cp++;
		    if (strchr( cp, SEQNO_TOKEN) ) {
			subs_seqno (rawreq.v.bat.stdlog_name, rawreq.orig_seqno);
		    }
		}
		i = strlen (rawreq.v.bat.stdout_name);
		if (i == 0 || rawreq.v.bat.stdout_name [i-1] == '/') {
			/*
			 *  The stdout file name needs to be resolved.
			 *  The user did NOT specify the pathname for
			 *  the stdout file, and the stdout file is to
			 *  be placed on the execution machine, or
			 *  returned to the submitting machine as
			 *  determined by the OMD_M_KEEP modifier bit.
			 */
			if (Debug > 1) {
				printf ("D$Stdout name needs resolution.\n");
				fflush (stdout);
			}
			if (resolvepath (&rawreq, &rawreq.v.bat.stdout_mid,
					 rawreq.v.bat.stdout_name,
					 rawreq.v.bat.stdout_acc, i,
					 'o') == -1) {
				/*
				 *  The resolved pathname will be too long.
				 */
				if (mode == 0) {
					rawreq.tcm = TCML_PATHLEN;
					writereq (cfd, &rawreq);
				}
				close (cfd);		/* Close request file */
				return (TCML_PATHLEN);
			}
			if (Debug > 1) {
				printf ("D$Resolved stdout name = %s.\n",
					 rawreq.v.bat.stdout_name);
				fflush (stdout);
			}
		} else {
		    /*
		     * Does the name include a hash token indicating that
		     * the request number should be substituted?
		     */
		    cp = strrchr (rawreq.v.bat.stdout_name,  '/');
		    if (cp == NULL) cp = rawreq.v.bat.stdout_name;
		    else cp++;
		    if (strchr( cp, SEQNO_TOKEN) ) {
			subs_seqno (rawreq.v.bat.stdout_name, rawreq.orig_seqno);
		    }
		}
	}
	/*
	 *  The request has been assigned a sequence number, and any
	 *  unresolved output return pathnames have also been resolved.
	 *
	 *  Try to insert the request into the internal queues of NQS.
	 */
	insqueres = nsq_enque (mode, cfd, &rawreq, 0, frommid,
			       rawreq.enter_time);
	if ((insqueres & XCI_FULREA_MASK) != TCML_SUBMITTED) {
		/*
		 *  The request was NOT submitted.
		 */
		if (Debug > 1) {
	            printf("D$Nqs_quereq: Request not submitted, reason = %o\n",
		       insqueres);
		    fflush(stdout);
		}
		if (mode == 0) {
			rawreq.tcm = insqueres;	/* Store transaction and */
						/* information completion */
						/* codes */
			writereq (cfd, &rawreq);/* Update request header */
		}
		close (cfd);			/* Close request file */
		return (insqueres);		/* Return transaction code */
	}
	/*
	 *  The request was successfully enqueued.  Make sure to increment
	 *  the next available request sequence number, if necessary.
	 */
	if (mode == 0) {
		rawreq.tcm = insqueres;	/* Store transaction and information */
					/* completion codes */
		/*
		 *  Update the next available sequence number.
		 */
		if (++Seqno_user > MAX_SEQNO_USER) Seqno_user = 0;
		udb_useqno (Seqno_user);	/* Update */
		/*
		 *  A Sequence number and transaction-id has been assigned
		 *  to the request, and the request has been successfully
		 *  enqueued.  Form links in the NQS data directory to all
		 *  data files that are a part of the request.
		 *
		 *  Note however that the control file has NOT been updated
		 *  with the assigned sequence number or transaction-id.  If
		 *  the system crashes after the links have been made, but
		 *  with the control file un-updated, then nqs_rbuild.c will
		 *  have to catch it later.
		 */
		if (Debug) {
			printf ("D$Trying to link in files.\n");
			fflush (stdout);
		}
		for (i=0; i < rawreq.ndatafiles; i++) {
			pack6name (newpath, Nqs_data,
				  (int) (rawreq.orig_seqno % MAX_DATASUBDIRS),
				  (char *) 0, (long) rawreq.orig_seqno, 5,
				  (long) rawreq.orig_mid, 6, i, 3);
                        requests_dir = getfilnam (Nqs_requests, SPOOLDIR);
                        if (requests_dir == (char *)NULL)
                                return (TCML_INTERNERR);
                        pack6name (oldpath, requests_dir, -1,
                                   ctrlname, 0L, 0, 0L, 0, i, 3);
                        relfilnam (requests_dir);
			if (link (oldpath, newpath) == -1) {
				/*
				 *  The link failed!
				 */
				printf ("E$Unable to form link to ");
				printf ("new request data file.\n");
				printf ("I$link (%s, %s) failed;\n",
					 oldpath, newpath);
				printf ("I$%s.\n", asciierrno());
				fflush (stdout);
				/*
				 *  Unlink any previous links to data files
				 *  for the new request.
				 */
				nqs_delrfs (rawreq.orig_seqno,
					    rawreq.orig_mid, i);
				rawreq.tcm = TCML_UNAFAILURE;
				writereq (cfd, &rawreq);
				close (cfd);	/* Close request file */
				nsq_unque();	/* Unqueue the request */
				return (TCML_UNAFAILURE);	/* Failure */
			}
			unlink (oldpath);	/* Unlink original */
		}
		/*
		 *  Form the link to the control file for the new request.
		 */
		pack6name (newpath, Nqs_control,
			  (int) (rawreq.orig_seqno % MAX_CTRLSUBDIRS),
			  (char *) 0, (long) rawreq.orig_seqno, 5,
			  (long) rawreq.orig_mid, 6, 0, 0);
                requests_dir = getfilnam (Nqs_requests, SPOOLDIR);
                sprintf (oldpath, "%s/%s", requests_dir, ctrlname);
                relfilnam (requests_dir);
		if (link (oldpath, newpath) == -1) {
			/*
			 *  We failed.
			 */
			printf ("E$Unable to form link to new request ");
			printf ("control file.\n");
			printf ("I$link (%s, %s) failed;\n", oldpath, newpath);
			printf ("I$%s.\n", asciierrno());
			fflush (stdout);
			/*
			 *  Unlink all of the data files and control files
			 *  for the new request.
			 */
			nqs_delrfs (rawreq.orig_seqno, rawreq.orig_mid,
				    rawreq.ndatafiles);
			rawreq.tcm = TCML_UNAFAILURE;
			writereq (cfd, &rawreq);
			close (cfd);		/* Close request file */
			nsq_unque();		/* Unqueue the request */
			return (TCML_UNAFAILURE);
		}				/* Failure */
		unlink (oldpath);		/* Unlink original */
	}
	/*
	 *  The request has been assigned a sequence number and a transaction
	 *  descriptor.  The request has also been queued, and all of the
	 *  control and data files have been linked in.  Rawreq.tcm holds
	 *  the transaction completion code.
	 */
	writereq (cfd, &rawreq);		/* Update request header */
	close (cfd);				/* Close request file */
	if (Debug > 2) {
		printf ("D$nqs_quereq: Exiting with seqno %d.\n", orig_seqno);
		fflush (stdout);
	}
	return (insqueres);			/* Return transaction code */
}


/*** resolvepath
 *
 *
 *	int resolvepath():
 *	Resolve batch request output file path (when necessary).
 *
 *	Returns:
 *		 0: Path resolved successfully.
 *		-1: Resolved path is too long.
 */
static int resolvepath (rawreq, mid_resolve, path_resolve, access_mode, length,
			suffixchar)
register struct rawreq *rawreq;		/* Raw request structure for request */
Mid_t *mid_resolve;			/* Machine-id requiring resolution */
register char *path_resolve;		/* Path requiring resolution */
short access_mode;			/* Access mode */
register int length;			/* Len of path requiring resolution */
char suffixchar;			/* Suffix char for default */
{
	char dirpath [MAX_REQPATH+1];	/* Output file pathname */

	if (length + 14 > MAX_REQPATH) {
		/*
		 *  The resolved pathname will be
		 *  too long.
		 */
		rawreq->tcm = TCML_PATHLEN;
		return (-1);		/* Too long */
	}
	strcpy (dirpath, path_resolve);
	if (length) dirpath [length-1] = '\0';
	/*
	 *  Manufacture proper default pathname.
	 */
	mkdefault (path_resolve, dirpath, rawreq->reqname,
		  (long) rawreq->orig_seqno, suffixchar);
	if (access_mode & OMD_M_KEEP) {
		/*
		 *  The file must be left on the execution machine.
		 */
		*mid_resolve = Locmid;
	}
	return (0);			/* Success */
}

/*** nqs_holdreq
 *
 *
 *	long nqs_holdreq()
 *
 *	Place a hold on an NQS request.
 *
 *	Returns:
 *		TCML_COMPLETE:	if the request was successfully held.
 *		TCML_NOSUCHREQ:	if the specified request does not exist
 *				on this machine.
 *		TCML_NOTREQOWN:	if the mapped user-id does not match
 *				the current mapped user-id of the
 *				request owner.
 */
long nqs_holdreq (mapped_uid, orig_seqno, orig_mid)
uid_t mapped_uid;			/* Mapped owner user-id */
long orig_seqno;			/* Original request sequence# */
Mid_t orig_mid;				/* Original machine-id of req */
{
	struct request *predecessor;	/* Predecessor in req set in queue */
	int state;			/* Request queue state: RQS_ */
	register struct nqsqueue *queue;   /* Queue in which req is placed */
	register struct request *req;	/* Request struct allocated for req */
	struct request *nqs_fndreq();

	/*
	 *  Locate the request.
	 */
	state = RQS_STAGING | RQS_QUEUED | RQS_WAITING | RQS_HOLDING |
		RQS_RUNNING;
	if ((req = nqs_fndreq (orig_seqno, orig_mid, &predecessor,
			       &state)) == (struct request *) 0) {
		/*
		 *  The request was not found in any of the local queues.
		 */
		return (TCML_NOSUCHREQ);
	} else if (state & RQS_RUNNING)
		return (TCML_REQRUNNING);	/* Request already running */
	if (mapped_uid && req->v1.req.uid != mapped_uid)
		/*
		 *  This request cannot be affected by the client.
		 */
		return (TCML_NOTREQOWN);
	queue = req->queue;
	switch (state) {
	case RQS_QUEUED:
		/*
		 *  The request is queued, so remove the request from the
		 *  queued set.
		 */
		if (predecessor == (struct request *) 0)
			queue->queuedset = req->next;
		else
			predecessor->next = req->next;
		queue->q.queuedcount--;		/* One less request queued */
		break;
	case RQS_WAITING:
		/*
		 *  The request is waiting to be started, so remove the request
		 *  from the waiting set.
		 */
		if (predecessor == (struct request *) 0)
			queue->waitset = req->next;
		else
			predecessor->next = req->next;
		queue->q.waitcount--;		/* One less request waiting */
		break;
	case RQS_HOLDING:
		/*
		 *  This request is already held.
		 */
		return (TCML_COMPLETE);
		break;
	}
	/*
	 *  Add the request to the holding set.
	 */
	a2s_a2hset (req, queue);
	req->status |= RQF_OPERHOLD;		/* Set Flag */
	/*
	 *  Set flags to indicate that the operator is holding this request.
	 */
	if (flag_upd (req, RQF_OPERHOLD, 0) != 0)
		return (TCML_UNAFAILURE);
	/*
	 *  The NQS database queue image needs to be updated.
	 */
	udb_qorder (queue);
	return (TCML_COMPLETE);
}


/*** nqs_relreq
 *
 *
 *	long nqs_relreq()
 *
 *	Remove a hold on an queued NQS request.
 *
 *	Returns:
 *		TCML_COMPLETE:	if the request was successfully released.
 *		TCML_NOSUCHREQ:	if the specified request does not exist
 *				on this machine.
 *		TCML_NOTREQOWN:	if the mapped user-id does not match
 *				the current mapped user-id of the
 *				request owner.
 *		TCML_REQNOTHELD:if the specified request is not held.
 */
long nqs_relreq (mapped_uid, orig_seqno, orig_mid)
uid_t mapped_uid;			/* Mapped owner user-id */
long orig_seqno;			/* Original request sequence# */
Mid_t orig_mid;				/* Original machine-id of req */
{
	struct request *predecessor;	/* Predecessor in req set in queue */
	int state;			/* Request queue state: RQS_ */
	register struct nqsqueue *queue;   /* Queue in which req is placed */
	register struct request *req;	/* Request struct allocated for req */
	struct request *nqs_fndreq();
	/*
	 *  Locate the request.
	 */
	state = RQS_STAGING | RQS_QUEUED | RQS_WAITING | RQS_HOLDING |
		RQS_RUNNING;
	if ((req = nqs_fndreq (orig_seqno, orig_mid, &predecessor,
			       &state)) == (struct request *) 0)
		/*
		 *  The request was not found in any of the local queues.
		 */
		return (TCML_NOSUCHREQ);
	if (mapped_uid && req->v1.req.uid != mapped_uid)
		/*
		 *  This request cannot be affected by the client.
		 */
		return (TCML_NOTREQOWN);
	if (!(state & RQS_HOLDING))
		/*
		 *  This request is not on hold.
		 */
		return (TCML_REQNOTHELD);
	queue = req->queue;
	/*
	 *  Remove the request to the holding set.
	 */
	if (predecessor == (struct request *) 0)
		queue->holdset = req->next;
	else
		predecessor->next = req->next;
	queue->q.holdcount--;			/* One less request held */
	req->status &= ~RQF_OPERHOLD;		/* Clear Flag */
	/*
	 *  Clear RQF_OPERHOLD.
	 */
	if (flag_upd (req, 0, RQF_OPERHOLD) != 0)
		return (TCML_UNAFAILURE);
	if (req->start_time > time ((time_t *) 0)) {
		/*
		 *  The request has a future start time, so add the request to
		 *  the waiting set.
		 */
		a2s_a2wset (req, queue);
	} else {
		/*
		 *  Add the request to the queued set.
		 */
		a2s_a2qset (req, queue);
		/*
		 *  Maybe spawn a request.
		 */
		switch (queue->q.type) {
		case QUE_BATCH:
			bsc_spawn ();
			break;
		case QUE_DEVICE:
			dsc_spawn ();
			break;
		case QUE_PIPE:
			psc_spawn ();
			break;
		}
	}
	/*
	 *  The NQS database queue image needs to be updated.
	 */
	udb_qorder (queue);
	return (TCML_COMPLETE);
}


/***    flag_upd
 *
 *
 *	int flag_upd()
 *
 *	Update the flags field in a raw request in a control file
 *
 *	Returns:
 *		 0:	if successful.
 *		-1:	if failure.
 */
static int flag_upd (req, setflags, clflags)
struct request *req;			/* Ptr to request structure */
short setflags;				/* Flags to set */
short clflags;				/* Flags to clear */
{
	int cfd;			/* File descriptor for control file */
	struct rawreq rawreq;		/* Raw request structure */

	/*
	 *   Locate and read raw request header.
	 */
	if ((cfd = gethdr (req->v1.req.orig_seqno, req->v1.req.orig_mid,
	     &rawreq)) == -1 ) {
		printf ("E$flag_upd(): Read of request header failed.");
		fflush (stdout);
		return (-1);
	}
	/*
	 *  Update flags.
	 */
	rawreq.flags |= setflags;
	rawreq.flags &= ~clflags;
	/*
	 *  Rewrite raw request to control file.
	 */
	if (writehdr (cfd, &rawreq) == -1) {
		printf ("E$flag_upd(): Write of request header failed.");
		fflush (stdout);
		close (cfd);
		return (-1);
	}
	close (cfd);
	return (0);
}
/*
 * This routine is used to take the file name and substitue tokens with
 * the ascii sequence number.
 */
static
void subs_seqno (name, seqno)
char *name;			/* Pointer to name containing 1 or more tokens */
int seqno;			/* Number to substitute (in ascii) for token */
{
    char *cp, *acp, *dcp;		/* Utility character pointers */
    char dirpath [MAX_REQPATH+1];	/* Output file pathname */
    char ascii_seqno[16];		/* Ascii form of seqno */
    
    sprintf(ascii_seqno, "%d", seqno);
    strcpy (dirpath, name);
    dcp = name;
    acp = strrchr (dirpath, '/');
    if (acp != NULL) {
	dcp = strrchr (name,  '/');
	dcp++; acp++;
    } else acp = dirpath;
    while (*acp) {
	if (*acp == SEQNO_TOKEN) {
	    acp++;			/* Skip token */	  
	    cp = ascii_seqno;		/* And put in ascii sequence number */
	    while (*cp) {
		*dcp++ = *cp++;
	    }
	} else *dcp++ = *acp++;
    }
    *dcp = '\0';
}
