/* cliutl.c */

#define	CLIUTL_DEF	1
#define	CLIUTLMASTER	1

/*
 * Includes
 */
#include <stdio.h>
#include <string.h>

#include "dfault.h"
#include "evtdef.h"
#include "hstdef.h"
#include "prodef.h"
#include "cliutl.h"
#include "cldrvr.h"
#include "mapskt.h"
#include "kbdutl.h"
#include "bytprc.h"
#include "cticks.h"
#include "suspnd.h"

/*
 * debug -
 *
 *	bit 0	(0x01)	enable printing of internal events
 */
int	debug = 0;

/*
 * size of timer queue
 */
#define NTIMES 30

/*
 * timer queue of events which will be placed
 * into the event queue when the time is up.
 */
static struct {
	int	eclass,		/* event queue data */
		event,
		idata,
		next;		/* next item in list */
	long	when;		/* when timer is to go off */
} Stq[NTIMES];

static	int	Stfirst,
		Stfree;		/* pointers for timer queue */


long	skquetime;		/* last time data transferred */

/*
 * Compute elapsed time
 */
long elapsed(ltm)
long ltm;
{
	long el;

	el = cticks(NULL)-ltm;
	if(el < 0)
		el += WRAPTIME;
	return(el);
}

/*
 *  mdemux
 *
 *  Find the messages in the buffer, queue up all classes.
 *
 *  The 'all' parameter tells demux whether it should attempt to empty
 *  the input message buffer or return after the first useful message
 *  is queued.
 *
 *  returns the number of messages queued
 */
int mdemux(all)
int all;
{
	register int nmuxed;
	register MSGPKT *pkt;

	nmuxed = 0;
	/*
	 * while all flag is on
	 */
	do {
		pkt = (MSGPKT *) cl_recv();
		if(pkt==NULL) {
			all = 0;
		} else {
			skptuev(pkt->class,pkt->event,pkt->data);
			nmuxed++;
		}
	} while(all);
	return(nmuxed);
}

/*
 *  socket
 *
 *  Send a request for a socket to TCPIP.
 *  If TCPIP responds with a TCPIP socket number,
 *  then the socket has been allocated space in the
 *  specified global region and has been initialized
 *  for a TCP connection.
 */

int socket(lpnum)
int lpnum;
{
	cl_xmit(SCKTCLASS,REQSCKT,tljobn);
	return(response(SCKTCLASS,USRSCKT,lpnum));
}

/*
 *  attach
 *
 *  This function is used only by programs started
 *  by TCPIP using the configuration file services
 *  option.
 *
 *  Send a request to TCPIP to attach to a service.
 *  If TCPIP responds with a TCPIP socket number,
 *  then the service has been attached
 *  to this process.
 */

int attach(lpnum,iport)
int lpnum,iport;
{
	int sknum;

	/*
	 * To allow verification of the attaching job
	 * the job number and port number are sent to TCPIP.
	 */
	cl_xmit(SRVCATTACH,iport,-tljobn);
	sknum = response(SRVCCLASS,ATTACHED,lpnum);
	if(sknum < 0) {
		kb_puts(skerrstring(501));
		kb_nline();
	}
	return(sknum);
}

/*
 *  response
 *
 *  Get a response to a TCPIP request
 */
int response(class,event,lpnum)
int class,event,lpnum;
{
	register struct socket *skt;
	int cl,ev,dat;
	long start;

	/*
	 * wait for response
	 */
	start = cticks(NULL);
	do {
		ev = Sgetevent(class|ERRCLASS|MSGCLASS,&cl,&dat);
		if(ev) {
			if(cl==class) {
				if(ev!=event) {
					skptevent(class,ev,dat);
					ev = 0;
				}
			} else {
				kb_puts(skerrstring(ev));
				kb_nline();
				ev = 0;
			}
		}
		if(ev==0)
			suspnd(0);
		if(elapsed(start) > RESPNDTIMEOUT) {
			kb_puts("No response from TCPIP Server\r\n");
			return(-1);
		}
	} while(ev==0);
	/*
	 * Verify the socket parameter
	 */
	if(dat<0 || dat>=GSCKTS)
		return(-1);
	/*
	 * Try to map the socket region
	 */
	skt = (struct socket *) mapskt(dat);
	if(skt==-1) {
		kb_puts(skerrstring(500));
		kb_nline();
		sktlist[dat].lpnum = -1;
		sktlist[dat].sktid = 0;
		return(-1);
	} else {
		sktlist[dat].lpnum = lpnum;
		sktlist[dat].sktid = skt->sktid;
		return(dat);
	}
}

/*
 *  connect
 *
 *  Request a connection
 */
VOID connect(sknum,st)
int sknum;
char *st;
{
	register struct socket *skt;

	/*
	 * Verify the socket parameter
	 */
	skt = (struct socket *) skvalid(sknum);
	if(skt==-1)
		return(-1);

	/*
	 * Copy machine description into the socket request buffer
	 */
	strncpy(skt->request,st,sizeof(skt->request));

	/*
	 * Request a connection
	 */
	cl_xmit(TCPCLASS,REQCONNECT,sktlist[sknum].sktid);
}

/*
 *  listen
 *
 *  Request to listen
 */
int listen(sknum,port)
register int sknum;
register int port;
{
	register struct socket *skt;

	/*
	 * Verify the socket parameter
	 */
	skt = (struct socket *) skvalid(sknum);
	if(skt==-1)
		return(-1);

	skt->sktport = port;
	/*
	 * Request to listen
	 */
	cl_xmit(TCPCLASS,REQLISTEN,sktlist[sknum].sktid);
	/*
	 * wait for SLISTEN state
	 */
	do {
		suspnd(6);
	} while(skt->state != SLISTEN);
	return(skt->sktport);
}

/*
 *  addsess
 *  Add a session to a named machine, or prompt for a machine to be named.
 */
int addsess(lpnum,st,port)
int lpnum;
char *st;
int port;
{
	register struct socket *skt;
	char s[80];
	int i,sknum;

	/*
	 * bound the local socket numbers
	 */
	if(lpnum<0 || lpnum>=LSCKTS)
		return(-1);

	if(st==NULL) {	    /* no machine yet */
	  kb_puts(
	    "\r\nEnter machine name/address, ESC to return: \r\n");
	  s[0] = '\0';
	  while(0>=(i = kb_gets(s,sizeof(s),1)))
	    Stask();
	  if(i==27 || !s[0])
	    return(-1);
	  kb_puts("\r\n\n");	/* skip down a little */
	  st = s;		/* make a copy of the pointer to s */
	}

	/*
	 * Request a TCPIP socket
	 */
	sknum = socket(lpnum);
	if(sknum<0)
		return(-1);
	
	/*
	 * Specify the connection type
	 */
	skt = (struct socket *) mapskt(sknum);
	skt->sktport = port;

	/*
 	 * Request a connection
	 */
	connect(sknum,st);

	return(sknum);
}

/*
 * Log the Session
 */
VOID logsession(sknum,st)
int sknum;
char *st;
{
	register struct socket *skt;

	/*
	 * check validity
	 */
	skt = (struct socket *) skvalid(sknum);
	if(skt==-1)
		return(-1);

	strncpy(&skt->request,st,70);
	skt->request[69] = '\0';
	cl_xmit(TCPCLASS,LOGSESSION,sktlist[sknum].sktid);
}

/*
 *  errhandle
 *
 *  write error messages to the console window
 */
VOID errhandle()
{
	int cl,ev,dat;

	while((ev = Sgetevent(ERRCLASS | MSGCLASS,&cl,&dat)) != 0) {
	  kb_puts(skerrstring(ev));
	  kb_nline();
  	}
}

/*
 *  enqueue
 *
 *  Used by write.
 *  QUEUESIZE is the size limitation of the advertised window.
 */
int enqueue(skt,buffer,nbytes)
register struct socket *skt;
register char *buffer;
int nbytes;
{
	int i;

	i = outroom(skt);
	if(i<=0 || nbytes==0)
		/*
		 * no room at the inn
		 */
		return(0);

	skquetime = cticks(NULL);

	if(nbytes>i)
		nbytes = i;
	/*
	 * room at end
	 */
	i = skt->out.queue - skt->out.where;
	i = QUEUESIZE - i;
	if(i<nbytes) {
		movebytes(skt->out.queue,buffer,i);
		movebytes(skt->out.where,(char *)(buffer+i),nbytes-i);
		skt->out.queue = skt->out.where + nbytes - i;
	} else {
		movebytes(skt->out.queue,buffer,nbytes);
		if(i==nbytes) {
			skt->out.queue = skt->out.where;
		} else {
			skt->out.queue += nbytes;
		}
	}
	/*
	 * more stuff here
	 */
	return(nbytes);
}

/*
 *  dequeue
 *
 *  used by read, this copies data out of the
 *  queue and then deallocates it from the queue.
 *
 *  returns number of bytes copied from the queue
 */
int dequeue(skt,buffer,nbytes)
register struct socket *skt;
register char *buffer;
int nbytes;
{
	int i,qbytes;

	qbytes = inqlen(skt);
	if(qbytes==0 || nbytes==0)
		return(0);

	skquetime = cticks(NULL);

	if(qbytes<nbytes)
		nbytes = qbytes;

	i = skt->in.queue - skt->in.where;
	i = QUEUESIZE - i;
	if(i<nbytes) {
		movebytes(buffer,skt->in.queue,i);
		movebytes((char *)(buffer+i),skt->in.where,nbytes-i);
		skt->in.queue = skt->in.where + nbytes - i;
	} else {
		movebytes(buffer,skt->in.queue,nbytes);
		if(i==nbytes) {
			skt->in.queue = skt->in.where;
		} else {
			skt->in.queue += nbytes;
		}
	 }
	return(nbytes);
}

/*
 *  skposterr
 *  place an error into the event q
 *  Takes the error number and puts it into the error structure
 */
void skposterr(num)
int num;
{
	if(skptevent(ERRCLASS,num,-1))
		/*
		 * only if we lost an event
		 */
		skptuev(ERRCLASS,502,-1);
}

/*
 *  skgtevent
 *
 *  Retrieves the next event (and clears it) which matches bits in
 *  the given mask.  Returns the event number or -1 on no event present.
 *  Also returns the exact class and the associated integer in reference
 *  parameters.
 *
 *  The way the queue works:
 *	There is always a dummy record pointed to by nnelast.
 *	When data is put into the queue, it goes into nnelast,
 *	then nnelast looks around for another empty one to obtain.
 *	It looks at nnefree first, then bumps one from nnefirst if necessary.
 *	When data is retrieved, it is searched from nnefirst to nnelast.
 *	Any freed record is appended to nnefree.
 */
int skgtevent(mask,retclass,retint)
int mask;
int *retclass,*retint;
{
	int i,j;

	i = nnefirst;
	j = 0;
	while(i!=nnelast) {
		if(mask&nnq[i].eclass) {
			if(i==nnefirst) {
				/*
				 * step nnefirst
				 */
				nnefirst = nnq[nnefirst].next;
			} else {
				/*
				 * bypass record i
				 */
				nnq[j].next = nnq[i].next;
			}
			nnq[i].next = nnefree;
			/*
			 * install in free list
			 */
			nnefree = i;
if(debug&0x01) {
	printf("\r\nskgt: class=%d, event=%d, dat=%d, i=%d, mask=%d\r\n",
		nnq[i].eclass, nnq[i].event, nnq[i].idata, i, mask);
}
			*retint = nnq[i].idata;
			*retclass = nnq[i].eclass;
			return(nnq[i].event);
		}
		j = i;
		i = nnq[i].next;
	}
	return(0);
}

/*
 *  skptevent
 *
 *  add an event to the queue.
 *  Will probably get the memory for the entry from the free list.
 *  Returns 0 if there was room, 1 if an event was lost.
 */
int skptevent(class,event,dat)
int class,event,dat;
{
	int i;

	i = nnelast;
if(debug&0x01) {
	printf("\r\nskpt: class=%d, event=%d, dat=%d, i=%d\r\n",
		class, event, dat, i);
}
	/*
	 * put data in
	 */
	nnq[i].eclass = class;
	nnq[i].event = event;
	nnq[i].idata = dat;
	/*
	 * there is a spot in free list
	 */
	if(nnefree>=0) {
		nnq[i].next = nnelast = nnefree;
		/*
		 * remove from free list
		 */
		nnefree = nnq[nnefree].next;
		return(0);
	} else {
		nnq[i].next = nnelast = nnefirst;
		/*
		 * lose oldest event
		 */
		nnefirst = nnq[nnefirst].next;
		return(1);
	}
}

/*
 *  skptuev
 *
 *  put a unique event into the queue
 *  First searches the queue for like events
 */
int skptuev(class,event,dat)
int class,event,dat;
{
	int i;

	i = nnefirst;
	while(i!=nnelast) {
		if(nnq[i].event==event &&
			nnq[i].eclass==class &&
			nnq[i].idata==dat)
			return(0);
		i = nnq[i].next;
	}
	return(skptevent(class,event,dat));
}

/*
 *  skeventinit ()
 *
 *  Setup all the pointers for the event queue -- makes a circular list
 *  which is required for error messages. ( called from Snetinit () )
*/

VOID skeventinit()
{
	int i;

	for(i=0; i<NEVENTS; i++)
		nnq[i].next = i+1;
	nnq[NEVENTS-1].next = -1;
	nnefirst = 0;
	nnelast = 0;
	nnefree = 1;
}

/*
 *  skerrstring
 *
 *  returns the string associated with a particular error number
 *
 *  error number is formatted %4d at the beginning of the string
 */
static char *errs[] = {
	"   0 (Error Code)",
	/* addsess() messages */
	"  10 Require machine name/address",
	"  11 Querying the Domain Name Server",
	"  12 No name server, cannot resolve IP address",
	"  13 Internal Sdomain() allocation error",
	"  14 Trying to open TCP connection",
	"  15 Could not open the connection",
	"  16 Domain lookup failed",
	"  17 Domain lookup successful",
	/* Local */
	" 500 Socket failed to map",
	" 501 Attach to service failed",
	" 502 Event queue filled, probably non-fatal",
	/* From TCP/IP Server */
	" 503 Socket allocation error",
	""};

static char errspace[80];	/* room for user-defined errors */

char *skerrstring(errno)
int errno;
{
	int i;
	char s[10];

	if(errno<0)
		return(errspace);
	sprintf(s,"%4d",errno);
	i = 1;
	do {
		if(!strncmp(errs[i],s,4))
			/*
			 * pointer to error message
			 */
			return(errs[i]+5);
		i++;
	/*
	 * until NULL found
	 */
	}while(*errs[i]);
	/*
	 * error unknown
	 */
	sprintf(errs[0],"%4d",errno);
	errs[0][4] = ' ';
	return(errs[0]);
}

/*
 *  skvalid
 *
 *  Verify that the socket number is valid
 *  and map the socket for access
 */
struct socket *skvalid(sknum)
register int sknum;
{
	register int lpnum;
	register struct socket *skt;

	/*
	 * check validity
	 */
	if(sknum<0 || sknum>=GSCKTS)
		return(-1);

	lpnum = sktlist[sknum].lpnum;

	if(lpnum<0 || lpnum>=LSCKTS)
		return(-1);

	skt = (struct socket *) mapskt(sknum);

	if(skt->sktid != sktlist[sknum].sktid)
		return(-1);

	if(skt->jobnum != tljobn)
		return(-1);

	return(skt);
}

/*
 *  skread
 *
 *  Read data from the queue buffer (specified by sknum)
 *  into the user buffer for up to n bytes.
 *
 *  Returns number of bytes read or <0 if error.
 */
int skread(sknum,buffer,n)
int sknum,n;
char *buffer;
{
	int i;
	register struct socket *skt;

	/*
	 * check validity
	 */
	skt = (struct socket *) skvalid(sknum);
	if(skt==-1)
		return(-2);

	/*
	 * read from queue buffer
	 */
	i = dequeue(skt,buffer,n);

	/*
	 * enter event if more data
	 */
	if(i) {
		if(inqlen(skt))
			skptuev(CONCLASS,CONDATA,sknum);

		return(i);
	}

	return(sktest(sknum));
}

/*
 *  skempty
 *
 *  Discard data in the queue buffer (specified by sknum)
 */
int skempty(sknum)
int sknum;
{
	register struct socket *skt;

	/*
	 * check validity
	 */
	skt = (struct socket *) skvalid(sknum);
	if(skt==-1)
		return(-1);

	skt->in.queue = skt->in.wtptr;

	if(skt->in.queue!=skt->in.rdptr)
		cl_xmit(TCPCLASS,DEQUEUE,sktlist[sknum].sktid);

	return(0);
}

/*
 *  skdeque
 *
 *  inform TCPIP of any dequeued data
 */
int skdeque(sknum)
int sknum;
{
	register struct socket *skt;

	/*
	 * check validity
	 */
	skt = (struct socket *) skvalid(sknum);
	if(skt==-1)
		return(-1);

	/*
	 * Dequeue any messages
	 */
	mdemux(1);

	/*
	 * Dequeue any data used
	 */
	if(skt->in.queue!=skt->in.rdptr)
		cl_xmit(TCPCLASS,DEQUEUE,sktlist[sknum].sktid);

	return(0);
}

/*
 *  Global push counter.
 *  Incremented each time skwrite or skwchar is called.
 *  Clear each time skenque is called.
 */
int pushsk;

/*
 *  skwrite
 *
 *  Write data into the output queue (specified by sknum).
 *  TCPIP will distribute the data onto the wire later.
 *
 *  Returns number of bytes sent or <0 if an error.
 */
int skwrite(sknum,buffer,n)
int sknum,n;
char *buffer;
{
	register struct socket *skt;

	/*
	 * check validity
	 */
	skt = (struct socket *) skvalid(sknum);
	if(skt==-1)
		return(-2);

	if(skt->state!=SEST)
		return(-1);

	pushsk++;
	return(enqueue(skt,buffer,n));
}

/*
 *  skwchar
 *
 *  Write data into the output queue (specified by sknum).
 *  TCPIP will distribute the data onto the wire later.
 *
 *  Returns number of bytes sent or <0 if an error.
 */
int skwchar(sknum,c)
int sknum,c;
{
	register struct socket *skt;

	/*
	 * check validity
	 */
	skt = (struct socket *) skvalid(sknum);
	if(skt==-1)
		return(-2);

	if(skt->state!=SEST)
		return(-1);

	pushsk++;
	return(enqueue(skt,(char *) &c,1));
}

/*
 *  skenque
 *
 *  Conditionally sets the push bit on the specified socket data,
 *  informs TCPIP of the enqueued data, and returns whether the
 *  queue is empty or how many bytes in the queue. <0 if an error.
 */
int skenque(sknum,pflag)
int sknum;
int pflag;
{
	register struct socket *skt;

	pushsk = 0;

	/*
	 * check validity
	 */
	skt = (struct socket *) skvalid(sknum);
	if(skt==-1)
		return(-2);

	/*
	 * Dequeue any messages
	 */
	mdemux(1);

	/*
	 * Trigger TCPIP into action
	 */
	if((skt->out.queue!=skt->out.wtptr) || (skt->out.push != pflag)) {
		skt->out.push = pflag;
		cl_xmit(TCPCLASS,ENQUEUE,sktlist[sknum].sktid);
	}

	return(QUEUESIZE - 1 - outroom(skt));
}	

/*
 *  skstate
 *
 *  Returns the state of the connection.  <0 if an error.
 */
int skstate(sknum)
int sknum;
{
	register struct socket *skt;

	/*
	 * check validity
	 */
	skt = (struct socket *) skvalid(sknum);
	if(skt==-1)
		return(-2);

	/*
	 * socket state
	 */
	return(skt->state);
}	

/*
 *  skqlen
 *
 *  Returns the number of bytes waiting to be read
 *  from the incoming queue for socket sknum.
 *  <0 if error.
 */
int skqlen(sknum)
int sknum;
{
	register struct socket *skt;

	/*
	 * check validity
	 */
	skt = (struct socket *) skvalid(sknum);
	if(skt==-1)
		return(-2);

	return(inqlen(skt));
}

int inqlen(skt)
register struct socket *skt;
{
	register int diff;

	diff = skt->in.wtptr - skt->in.queue;
	if(diff>=0) {
		return(diff);
	} else {
		return(diff+QUEUESIZE);
	}
}

/*
 *  skroom
 *
 *  Returns the space available in
 *  the outgoing queue for socket sknum.
 *  <0 if error.
 */
int skroom(sknum)
int sknum;
{
	register struct socket *skt;

	/*
	 * check validity
	 */
	skt = (struct socket *) skvalid(sknum);
	if(skt==-1)
		return(-2);

	return(outroom(skt));
}

/*
 *  skqued
 *
 *  Returns:
 *	0	if socket contains less than skt->sendsize bytes
 *	1	if socket contains more than skt->sendsize bytes
 *	<0 if error.
 */
int skqued(sknum)
int sknum;
{
	register struct socket *skt;

	/*
	 * check validity
	 */
	skt = (struct socket *) skvalid(sknum);
	if(skt==-1)
		return(-2);

	return((QUEUESIZE-1-outroom(skt)) >= skt->sendsize ? 1 : 0);
}

int outroom(skt)
register struct socket *skt;
{
	register int diff;

	diff = skt->out.queue - skt->out.rdptr;
	if(diff>=0) {
		return(QUEUESIZE-1-diff);
	} else {
		return(-1-diff);
	}
}

/*
 *  sktest
 *
 *  Checks to see if a particular session has been established yet.
 *  Returns 0 if the connection is in "established" state.
 */
int sktest(sknum)
int sknum;
{
	register struct socket *skt;
	long time;

	/*
	 * check validity
	 */
	skt = (struct socket *) skvalid(sknum);
	if(skt==-1)
		return(-2);

	if(skt->state==SEST) {
		return(0);
	} else {
		if(skt->state==SCWAIT) {
			if(inqlen(skt)) {
				return(0);
			}
			cl_xmit(TCPCLASS,CLOSING,
			      sktlist[sknum].sktid);
			time = cticks(NULL);
			while(skt->state == SCWAIT &&
			      elapsed(time) < 300L) {
				mdemux(1);
				suspnd(0);
			}
		}
	}
	return(-1);
}

/*
 *  sksendwait
 *
 *  Invalid socket		- return(-2)
 *  Connection dropped		- return(-1)
 *  Data sent			- return( 0)
 *  Timeout			- return( 1)
 */
int sksendwait(sknum,wt)
int sknum;
long wt;
{
	register struct socket *skt;
	long time;

	/*
	 * check validity
	 */
	skt = (struct socket *) skvalid(sknum);
	if(skt==-1)
		return(-2);

	/*
	 * Wait until connection dropped, data sent, or timeout
	 */
	time = cticks(NULL);
	while(1) {
		if(!skenque(sknum,1))
			return(0);
		if(sktest(sknum))
			return(-1);
		if(elapsed(time) >= wt)
			return(1);
		mdemux(1);
		suspnd(0);
	}
}

/*
 *  skrurgent
 *
 *  Returns pointer to urgent input data
 */
char *skrurgent(sknum)
int sknum;
{
	register struct socket *skt;

	/*
	 * check validity
	 */
	skt = (struct socket *) skvalid(sknum);
	if(skt==-1)
		return(NULL);

	return(skt->in.urgent);
}

/*
 *  skwurgent
 *
 *  Loads the output urgent data flag
 */
int skwurgent(sknum,i)
int sknum,i;
{
	register struct socket *skt;

	/*
	 * check validity
	 */
	skt = (struct socket *) skvalid(sknum);
	if(skt==-1)
		return(0);

	skt->out.urgent = i;
	return(1);
}

/*
 *  skclose
 *
 *  Start the closing process on socket sknum.
 */
VOID skclose(sknum)
int sknum;
{
	register struct socket *skt;

	/*
	 * Verify the socket parameter
	 */
	skt = (struct socket *) skvalid(sknum);
	if(skt==-1)
		return;

	cl_xmit(TCPCLASS,CLOSING,sktlist[sknum].sktid);
}

/*
 *  skabort
 *
 *  Abort the socket connection for sknum.
 */
VOID skabort(sknum)
int sknum;
{
	register struct socket *skt;

	/*
	 * Verify the socket parameter
	 */
	skt = (struct socket *) skvalid(sknum);
	if(skt==-1)
		return;

	cl_xmit(SCKTABORT,tljobn,sktlist[sknum].sktid);
}

/*
 *  skrelease
 *
 *  Finish the closing process on socket sknum.
 *	1 - issue a close if in state STWAIT/STLAST
 *	2 - return(-1) if in state SCLOSED
 *	3 - loop until a timeout if flag is set,
 *	    then issue a close and release the socket
 *
 *	return -1 if the socket is closed or released
 *	else return sknum
 */
int skrelease(sknum,tflag)
int sknum,tflag;
{
	register struct socket *skt;
	long time;

	/*
	 * check validity
	 */
	skt = (struct socket *) skvalid(sknum);
	if(skt==-1)
		return(sktlist[sknum].lpnum = -1);

	time = cticks(NULL);
	do {
		switch(skt->state) {
		default:		/* check for timeout */
			if(elapsed(time) < (long) RELEASETIME) {
				break;
			}

		case SLAST:		/* close the socket */
		case STWAIT:
			skclose(sknum);

 		case SCLOSED:		/* socket is released */
			return(sktlist[sknum].lpnum = -1);
			break;

		}
		if(tflag)
			suspnd(15);
	} while(tflag);
	return(sknum);
}

/*
 *  skgtftp
 *
 *  This routine provides the information needed to open an ftp connection
 *  back to the originator of the command connection.  The other side's IP
 *  number and the port numbers are returned in an INTEGER array ( convenient
 *  for use in PORT commands ).
 */
int skgtftp(sknum,a)
int sknum,a[];
{
	register struct socket *skt;
	register unsigned int i;

	/*
	 * check validity
	 */
	skt = (struct socket *) skvalid(sknum);
	if(skt==-1)
		return(-2);

	a[0] = skt->tcpout.i.ipdest[0] & 0xFF;
	a[1] = skt->tcpout.i.ipdest[1] & 0xFF;
	a[2] = skt->tcpout.i.ipdest[2] & 0xFF;
	a[3] = skt->tcpout.i.ipdest[3] & 0xFF;
	i = intswap(skt->tcpout.t.source);
	a[4] = i>>8 & 0xFF;
	a[5] = i    & 0xFF;
	i = intswap(skt->tcpout.t.dest);
	a[6] = i>>8 & 0xFF;
	a[7] = i    & 0xFF;

	return(0);
}

/*
 *  Force the from port on the connection
 */
int skfromport(sknum,fromport)
int sknum,fromport;
{
	register struct socket *skt;

	/*
	 * check validity
	 */
	skt = (struct socket *) skvalid(sknum);
	if(skt==-1)
		return(-2);

	/*
	 * allow the from port to be forced
	 */
	skt->skfport = fromport;

	return(0);
}

/*
 *  Snetinit
 *
 *  Handle all the initialization:
 *  internal queues and parameters.
 */
VOID Snetinit()
{
	int i;

	/*
	 * no ports allocated to this job
	 */
	for(i=0; i<GSCKTS; i++) {
		sktlist[i].lpnum = -1;
		sktlist[i].sktid =  0;
	}
	/*
	 * initializes for error messages to count
	 */
	skeventinit();
	/*
	 * load linked list
	 */
	for(i=0; i<NTIMES; i++)
		Stq[i].next = i+1;

	Stq[NTIMES-1].next = -1;
	Stfirst = -1;
	Stfree = 0;
	/*
	 * clear timeout
	 */
	skquetime = 0L;
}

/*
 *  Stask
 *
 *  A higher level version of net sleep -- manages the timer queue.
 *  Always call this in your main loop.
 */
static long recent = 0L;

VOID Stask()
{
	long t;
	int i;

	/*
	 * Check the timer queue to see if something should be posted
	 * First check for timer wraparound
	 */
	t = cticks(NULL);
	if(t<recent) {
		i = Stfirst;
		while(i>=0) {
			Stq[i].when -= WRAPTIME;
			i = Stq[i].next;
		}
	}
	recent = t;

	/*
	 * Q is not empty and timer is going off
	 */
	while (Stfirst >= 0 && t > Stq[Stfirst].when) {
		i = Stfirst;
		skptevent(Stq[i].eclass,Stq[i].event,Stq[i].idata);
		/*
		 * remove from q
		 */
		Stfirst = Stq[Stfirst].next;
		Stq[i].next = Stfree;
		/*
		 * add to free list
		 */
		Stfree = i;
	}
}

/*
 *  Stmrset
 *
 *  Set an async timer which is checked in Stask -- when time elapses
 *  sticks an event in the network event queue
 *
 *  class, event, dat is what gets posted when howlong times out.
 */
int Stmrset(class,event,dat,howlong)
int class,event,dat,howlong;
{
	int	i,j,jlast,retval;
	long	gooff;

	retval = 0;
	gooff = cticks(NULL)+howlong*TICKSPERSEC;
	/*
	 * queue is full, post first event
	 */
	if(Stfree < 0) {
		Stfree = Stfirst;
		Stfirst = Stq[Stfirst].next;
		Stq[Stfree].next = -1;
		skptevent(
			Stq[Stfree].eclass,
			Stq[Stfree].event,
			Stq[Stfree].idata);
		retval = -1;
	  }
	/*
	 * event to occur at that time
	 */
	Stq[Stfree].idata = dat;
	Stq[Stfree].event = event;
	Stq[Stfree].eclass = class;
	Stq[Stfree].when = gooff;
	/*
	 * remove from free list
	 */
	i = Stfree;
	Stfree = Stq[i].next;
	if(Stfirst < 0) {
		/*
		 * if no queue yet anchor active q
		 */
		Stfirst = i;
		Stq[i].next = -1;
	}
	else 
		/*
		 * goes first on list at beginning of list
		 */
		if(gooff < Stq[Stfirst].when) {
			Stq[i].next = Stfirst;
			Stfirst = i;
		} else {
			/*
			 * goes in middle
			 * search q from beginning
			 */
			j = jlast = Stfirst;
			while (gooff >= Stq[j].when&&j >= 0) {
				jlast = j;
				j = Stq[j].next;
			}
			/*
			 * insert in q
			 */
			Stq[i].next = j;
			Stq[jlast].next = i;
		}
	return(retval);
}

/*
 *  Stmrunset
 *
 *  Remove all timer events from the queue
 *  that match the class/event/dat.
 */
int Stmrunset(class,event,dat)
int class,event,dat;
{
	int i,ilast,retval;

	retval = ilast = -1;
	i = Stfirst;
	/*
	 * search list
	 */
	while (i >= 0 ) {
		if(Stq[i].idata == dat &&
		   Stq[i].eclass == class &&
		   Stq[i].event == event) {
			/*
			 * found at least one
			 */
			retval = 0;
			if(i == Stfirst) {
				/*
				 * first one matches
				 * attach to free list
				 */
				Stfirst = Stq[i].next;
				Stq[i].next = Stfree;
				Stfree = i;
				i = Stfirst;
				/*
				 * start list over
				 */
				continue;
			} else {
				/*
				 * remove this entry
				 * attach to free list
				 */
				Stq[ilast].next = Stq[i].next;
				Stq[i].next = Stfree;
				Stfree = i;
				i = ilast;
			}
		}
		ilast = i;
		i = Stq[i].next;
	}
	return(retval);
}

/*
 *  Sgetevent
 *
 *  Run Stask()
 *  Gets events from session or TCPIP
 */
int Sgetevent(mask,retclass,retdat)
int mask;
register int *retclass,*retdat;
{
	/*
	 * allow messages
	 */
	mdemux(1);
	/*
	 * allow timers to take place
	 */
	Stask();
	/*
	 * session event
	 */
	return(skgtevent(mask,retclass,retdat));
}
                                         