/* rlpd.c */

#define	RLPDMASTER

/*
 *  Includes
 */
#include <stdio.h>
#include <string.h>

#include "os.h"
#include "vrsion.h"
#include "vrlpd.h"
#include "dfault.h"
#include "evtdef.h"
#include "hstdef.h"
#include "prodef.h"
#include "cliutl.h"
#include "tsxutl.h"
#include "kbdutl.h"
#include "rtfile.h"
#include "rcdlck.h"
#include "datime.h"
#include "inicli.h"
#include "suspnd.h"

/*
 * Internal routines
 */

/*
 * Due to DECUS C aborts from lack of space
 * all functions/subroutines of type 'int'
 * are no longer explicitly defined.
 *
 * DECUS C defaults to 'int', VOID == int
 */

#if 0
extern	VOID	printtxt();		/* (txt) print text */
extern	int	dosess();		/* session loop */
extern	int	setlpd();		/* setup LPD session */
extern	VOID	lpdresp();		/* (s) response to client */
extern	VOID	lsterr();		/* error lister */
extern	int	response();		/* dumby routine for TSXUTL.C */
extern	VOID	lpd();			/* (code) process LPD commands */
extern	int	getword();		/* (string,word) extract a word */
extern	VOID	strlwr();		/* (st) convert to lower case */
extern	int	checkque();		/* (que,host,user) check queue list */
extern	VOID	*new();			/* (n) allocate n bytes */
extern	int	lpdsequence();		/* get next LPD sequence */
extern	VOID	lpdone();		/* finish, start lprint */
extern	VOID	getinfo();		/* (fp) get info from cfA file */
extern	VOID	questat();		/* get status of queue */
extern	VOID	rmvjob();		/* remove print jobs from queues */
extern	VOID	rcvbyt();		/* (i) receive a single byte */
extern	VOID	rcvcmnd();		/* (i) receive a command line */
extern	VOID	rcvcfA();		/* (i) receive a cfA file */
extern	VOID	rcvdfA();		/* (i) receive a dfA file */
extern	VOID	loadcmnd();		/* load a command sequence */
#endif

extern	char	*stptok();		/* (p,t,l,d) stop on token */
extern	char	*stpblk();		/* (ch) stop on block */
extern	struct	lstruct	*lookup();	/* (id) lookup local file name */

/*
 * List of RLPD states
 */
#define	RCVBYTE		(-1)
#define	RCVCMND		(-2)
#define	RCVCFA		(-3)
#define	RCVDFA		(-4)
#define	LOADCMND	(-5)
#define	PJOBCMND	(-6)
#define	CFAEOF		(-7)
#define	DFAEOF		(-8)

#define	WAITING		(0)
#define	PRNTJOB		(1)
#define	RCVPJOB		(2)
#define	SNDQSHRT	(3)
#define	SNDQLONG	(4)
#define	RMVJOB		(5)
#define	DONE		(6)

#define	J_ABORT		(1 + 256)
#define	J_CFA		(2 + 256)
#define	J_DFA		(3 + 256)

#define	C_CLASS		('C')
#define	C_HOST		('H')
#define	C_INDENT	('I')
#define	C_JOB		('J')
#define	C_BANNER	('L')
#define	C_MAIL		('M')
#define	C_SOURCE	('N')
#define	C_USER		('P')
#define	C_QUEUE		('Q')
#define	C_SLINK		('S')
#define	C_TITLE		('T')
#define	C_ULINK		('U')
#define	C_WIDTH		('W')
#define	C_ZOPT		('Z')
#define	C_RTROFF	('1')
#define	C_ITROFF	('2')
#define	C_BTROFF	('3')
#define	C_STROFF	('4')
#define	C_CIF		('c')
#define	C_DVI		('d')
#define	C_PRINT		('f')
#define	C_PLOT		('g')
#define	C_BUPLOT	('k')
#define	C_CONTROL	('l')
#define	C_DITROFF	('n')
#define	C_POSTSCRIPT	('o')
#define	C_PR		('p')
#define	C_FORTRAN	('r')
#define	C_TROFF		('t')
#define	C_RASTER	('v')
#define	C_PALLADIUM	('z')

/*
 *	Defines
 */

#define	TIMEOUT		(600)	/* approximately 5 minutes */
#define	QUEWAIT		(20)	/* approximately 10 seconds */

/*
 *	Global Variables For RLPD Control Channel
 */

static int
	aflag = 0,		/* Attach to Service Flag */
	rflag = 0,		/* RLPD restart flag */
	lpdcskt = -1,		/* socket for incoming RLPD */
	mflag = 0,		/* monitor processing */
	cmndbyte = -1,		/* LPD command byte */
	ackbyte = 0,		/* LPD ACK/NAK byte */
	ackseq = 0,		/* cfA/dfA file ACK/NAK byte */
	timeout = 0,		/* Inactivity timeout counter */
	rcvpos = 0,		/* marker for gathering strings from net */
	rcnt = 0,		/* number of characters from control */
	rfstate = 0,		/* state of the control channel */
	retstate = 0;		/* to emulate a subroutine call */

static	long
	rcvcnt = 0L,		/* number of bytes received */
	cfAcnt = 0L,		/* number of bytes expected for cfA */
	dfAcnt = 0L;		/* number of bytes expected for dfA */

/*
 *	Global Buffers / Pointers
 */

#define	LBUFFER		(512)
#define BUFFERS		(128)
#define	STRLEN		(80)
#define	LISTSIZE	(4)

static char
	rcvstr[LBUFFER],	/* command string */
	scratch[BUFFERS],	/* scratch string */
	quename[BUFFERS],	/* current quename */
	username[BUFFERS];	/* current user name */

char	xs[2];			/* dumby buffer for TSXUTL.C */

static	int			/* printer agent task number */
	lprint = 0;

static char *			/* RLPD configuration file */
	lpdcfl = "TCP:lpdqrm.cfg";
static FILE *			/* RLPD configuration file handle */
	lpdcfh = NULL;

static char			/* RLPD temporary file directory */
	lpddir[BUFFERS];

static	char			/* control file name */
	cfAfl[BUFFERS];
static	FILE
	*cfAfh = NULL;		/* control file handle */

static	char
	dfAfl[BUFFERS];		/* data file name */
static	FILE
	*dfAfh = NULL;		/* data file handle */

static	char
	delfl[BUFFERS];		/* delete file name */

struct	cinfo {
	char	que[STRLEN];
	char	host[STRLEN];
	char	user[STRLEN];
	char	src[LISTSIZE][STRLEN];
	char	file[LISTSIZE][STRLEN];
};

static	struct	cinfo	cfAinfo;

struct	qinfo {
	char	que[STRLEN];
	char	host[STRLEN];
	char	user[STRLEN];
};

static	struct	qinfo	queinfo;

static	char	list[LISTSIZE][STRLEN];

struct	lstruct {
	struct lstruct	*next;
	char		lpdfile[20];
	char		lclfile[12];
	char		tmpfile[12];
};

static	struct lstruct *look = NULL;

/*
 * Debugging options
 *
 *	bit 0	(0x01)	event printing
 *	bit 1	(0x02)	dosess() events
 *	bit 2	(0x04)	lpd()
 *	bit 3	(0x08)	checkque()
 *	bit 4	(0x10)	questat()
 *	bit 5	(0x20)	rmvjob()
 */

#if ts$sys
static char *usetxt[] = {
       "",
#ifdef	DEBUGOPTION
       "  RLPD [?] [-d level] [-p filespec] [-t n] [-aerv]",
       "	?               List the Help Text and Exit RLPD",
       "	d  level	Debug Level",
       "		1 -	enable event printing",
       "		2 -	enable dosess() event printing",
       "		4 -	enable LPD() printing",
       "	        8 -	enable checkque printing",
       "	       16 -	enable questat printing",
       "	       32 -	enable rmvjob printing",
#else
       "  RLPD [?] [-p filespec] [-t n] [-aerv]",
       "	?               List the Help Text and Exit RLPD",
#endif
       "	a		Attach to Service",
       "	e		Enable Monitoring of RLPD Transactions",
       "	p  filespec	Specify the Configuration Filespec",
       "	r		Restart RLPD after Disconnect",
       "	v		Verbose Mode",
       "	t  n		TCPIP Task Number for RLPD Delivery Agent",
       "	v		Verbose Mode",
       "",
       0
       };
#endif

#if rt$sys
static char *usetxt[] = {
       "",
#ifdef	DEBUGOPTION
       "  RLPD [?] [-d level] [-p filespec] [-erv]",
       "	?               List the Help Text and Exit RLPD",
       "	d  level	Debug Level",
       "		1 -	enable event printing",
       "		2 -	enable dosess() event printing",
       "		4 -	enable LPD() printing",
       "	        8 -	enable checkque printing",
       "	       16 -	enable questat printing",
       "	       32 -	enable rmvjob printing",
#else
       "  RLPD [?] [-p filespec] [-erv]",
       "	?               List the Help Text and Exit RLPD",
#endif
       "	e		Enable Monitoring of RLPD Transactions",
       "	p  filespec	Specify the Configuration Filespec",
       "	r		Restart RLPD after Disconnect",
       "	v		Verbose Mode",
       "",
       0
       };
#endif

/*
 *  printout text
 */
VOID printtxt(txt)
char **txt;
{
	register char **dp;

	for (dp = txt; *dp; dp++) {
		if (**dp == '_') {
			kb_puts(*dp + 1);
		} else {
			kb_puts(*dp);
			kb_nline();
		}
	}
}


main(argc,argv)
int argc;
char *argv[];
{
	char c;
	int ev;
	register int i,j,k;

	/*
	 * Initialize for TSX+ clients
	 */
	inicli();

	/*
	 * Initialize kb handler
	 * Set the break character to ^C
	 */
	kb_init(0x03);

	/*
	 * Verbose Mode Off
	 */
	kb_prnt = 0;

	/*
	 * parse arguments
	 */
	for(i=1; i<argc; i++) {
		if(argv[i][0] == '?') {
		    kb_prnt = 1;
		    printtxt(usetxt);
		    exit(0);
		} else
		if(argv[i][0] == '-') {
		    j = i;
		    k = 1;
		    while((c = argv[j][k]) != '\0') {
			switch(tolower(c)) {
#if ts$sys
			case 'a':	/* attach to service */
				aflag = 1;
				break;
#endif
#ifdef	DEBUGOPTION
			case 'd':	/* debug, optional level */
				if(sscanf(argv[++i],"%d",&debug) <= 0)
					debug = -1;
				break;
#endif
			case 'e':	/* enable RLPD monitoring */
				mflag = 1;
				break;

			case 'p':	/* configuration file */
				lpdcfl = argv[++i];
				break;

			case 'r':	/* enable RLPD restarting */
				rflag = 1;
				break;
#if ts$sys
			/*
			 * This is a 'hidden' option for setting the
			 * the default subdirectory base 'ld' unit
			 * and setting the maximum subdirectory nesting
			 *	note:	subdbase + subdnest <= 8
			 */
			case 's':
				sscanf(argv[++i],"%d",&subdbase);
				sscanf(argv[++i],"%d",&subdnest);
				break;
#endif
#if ts$sys
			case 't':	/* task number */
				if(sscanf(argv[++i],"%d",&lprint) <= 0)
					lprint = 0;
				break;
#endif
			case 'v':	/* verbose mode on */
				kb_prnt = 1;
				break;

			default:	/* unknown option */
				printf(
				"Unrecognized option -%c ignored\r\n", c);
				break;
			}
			k++;
		    }
		}
	}

#ifdef	DEBUGOPTION
	if (debug)
		kb_prnt = 1;
#endif

	/*
	 * identify self
	 */
	printtxt(vrstxt);

	/*
	 *  Start listening to the RLPD port
	 */
	while(((lpdcskt = setlpd()) < 0) && !fndbrk) {
		/*
		 * Could not get a socket, wait 5 seconds
		 */
		suspnd(300);
	}

	while(!fndbrk && ev != -1) {
		ev = dosess();
		if(ev==0)
			suspnd(0);
	}

	/*
	 * Activate Printing
	 */
	lpdone();

	if(fndbrk) 
		kb_puts("\r\n^C\r\n");

	skclose(lpdcskt);
	skrelease(lpdcskt,1);

	suspnd(0);
	errhandle();

	cd("dk:");

	kb_puts("\r\nEscaping from RLPD\r\n");
	exit(0);
}

/*
 *  dosess
 *
 *  dosess is an infinite loop serving message events
 */
int dosess()
{
	int  cl,ev,dat;

	/*
	 * Check for any relevant messages
	 * that need to be handled by me
	 */
	ev = Sgetevent(SCKTCLASS | CONCLASS | USERCLASS |
			ERRCLASS | MSGCLASS | ABORTCLASS, &cl, &dat);
	if(ev!=0) {

#ifdef	DEBUGOPTION
if(debug&0x02) {
	printf("dosess(): cl = %d, ev = %d, dat = %u\r\n", cl,ev,dat);
}
#endif

	  switch(cl) {
	    case CONCLASS:
	      /*
	       * RLPD Connection socket
	       */
	      lpd(ev);
	      break;

	    case MSGCLASS:	/* messages */
	    case ERRCLASS:	/* error message */
	      kb_puts(skerrstring(ev));
	      kb_nline();
	      break;

	    case ABORTCLASS:	/* abort */
	      switch(ev) {
		case CLIENT:
		  /*
		   * disable automatic restart
		   */
		  rflag = 0;
		  kb_puts("\r\nRLPD session aborted by TCPIP\r\n");
		  Stmrset(ABORTCLASS,EXITABORT,dat,5);

		case USERABORT:
		  if(skrelease(dat,0) < 0) {
		    return(-1);
		  } else {
		    skclose(dat);
		    Stmrset(ABORTCLASS,USERABORT,dat,1);
		  }
		  break;

		case EXITABORT:
		  return(-1);
		  break;

		default:
		  break;
	      }
	      break;

	    default:
	      break;
	  }
	} else {
	  lpd(0);
	}

	return(ev);
}

/*
 *	set up to receive a connection for RLPD commands
 */
int setlpd()
{
	register int sknum;

	rfstate = WAITING;	/* state of the control channel */
	retstate = WAITING;	/* to emulate a subroutine call */
	rcnt = 0;		/* number of characters from control */
	rcvpos = 0;		/* character position */

	/*
	 * Set the default directory
	 */
	cd("dk:");

	/*
	 * Initialize various things
	 */
	Snetinit();

	/*
	 * aflag = 1	indicates attach mode
	 * aflag = 0	indicates listen mode
	 */
	if(aflag) {
		/*
		 * Attach job to connection opened by TCPIP
		 */
		sknum = attach(0,HLPD);
		if(sknum==-1) {
			fndbrk++;
			return(-1);
		} else {
			Stmrset(ABORTCLASS,CLIENT,sknum,CONNWAITTIME);
		}
	} else {
		/*
		 * Create socket for control connection
		 */
		sknum = socket(0);
		if(sknum==-1)
			return(-1);
		/*
		 * default port
		 */
		listen(sknum,HLPD);
	}
	return(sknum);

}

/*
 * Dump response to client
 */
VOID lpdresp(s)
register char *s;
{
	skwrite(lpdcskt, s, strlen(s));
	skwrite(lpdcskt, "\n", 1);
	skenque(lpdcskt, 1);
	if(mflag) {
		kb_puts(s);
		kbprintf("  {%d}\r\n", strlen(s)+2);
	}
}

/*
 * This is the error print routine for TSXUTL.C
 * It will be called only if there is an internal error
 */
VOID lsterr()
{
	if(*errstr) {
		kb_puts(errstr);
		kb_nline();
		errstr[0] = '\0';
	}
}

/*
 * This routine is a dumby routine for TSXUTL.C
 * It should never be called, but returns a 1 [ABORT]
 */
int rtresp()
{
	kb_puts("rtresp: This dumby routine should never be called");
	return(1);
}

/*
 *	Remote Printer Server
 */
VOID lpd(code)
int code;
{
	char word[STRLEN];
	struct lstruct *sp;

	timeout += 1;

#ifdef	DEBUGOPTION
if(debug&0x04) {
	if (rfstate) {
		printf("lpd(%d): rfstate = %d, str = %s\r\n",
			code,rfstate,rcvstr);
	}
}
#endif

	switch(rfstate) {
	case WAITING:

/*S*/	switch(code) {
	case CONOPEN:
		Stmrunset(ABORTCLASS,CLIENT,lpdcskt);

		if(NULL == (lpdcfh = rtopen(lpdcfl,"r",0,0))) {
#ifdef	DEBUGOPTION
if(debug&0x04) {
	printf("lpd(): no lpdcfl: %s\r\n", lpdcfl);
}
#endif
			rfstate = DONE;
			break;
		}
		/*
		 * First line is sequence number
		 */
		fgetss(scratch,sizeof(scratch)-1,lpdcfh);
		/*
		 * Second line is temporary file directory
		 */
		if (NULL != fgetss(scratch,sizeof(scratch)-1,lpdcfh)) {
			if (!getword(scratch,lpddir)) {
				strcpy(lpddir,"sy:");
			}
		}
		rtclose(lpdcfh);

		/*
		 * Change to LPD temporary file directory
		 */
		if (cd(lpddir)) {
			rfstate = DONE;
			break;
		}
			
		rcvcmnd(LOADCMND);
		break;

	case CONCLOSE:
		kb_puts("Connection Closed by Remote Client\r\n");
		rfstate = DONE;
		break;

	case CONFAIL:
		kb_puts("Connection Failed\r\n");
		rfstate = DONE;
		break;

	default:
		break;
/*S*/	}
		break;

	/*
	 * Print any waiting jobs
	 */
	case PRNTJOB:
		/*
		 * Activate Printing
		 */
		cl_xmit(TASKCLASS,STARTJOB,lprint);
		rfstate = DONE;
		break;

	/*
	 * Receive Print Job
	 */
	case RCVPJOB:
		rcvcmnd(PJOBCMND);
		break;

		/*
		 * Abort Job
		 */
		case J_ABORT:
			while (look != NULL) {
				delfil(0,look->tmpfile,0,0);
				sp = look;
				look = look->next;
				free(sp);
			}
			rcvcmnd(PJOBCMND);
			break;

		/*
		 * Receive Control File
		 */
		case J_CFA:
			if (getword(rcvstr,word)) {
				sscanf(word,"%ld", &cfAcnt);
				if (cfAcnt != 0L) {
					rcvcfA(CFAEOF);
					break;
				}
			}
			rcvcmnd(PJOBCMND);
			break;

		/*
		 * Receive Data File
		 */
		case J_DFA:
			if (getword(rcvstr,word)) {
				sscanf(word,"%ld", &dfAcnt);
				if (dfAcnt != 0L) {
					rcvdfA(DFAEOF);
					break;
				}
			}
			rcvcmnd(PJOBCMND);
			break;

	/*
	 * Send Queue Short
	 */
	case SNDQSHRT:
		questat();
		rfstate = DONE;
		break;

	/*
	 * Send Queue Long
	 */
	case SNDQLONG:
		questat();
		rfstate = DONE;
		break;

	/*
	 * Remove Print Job
	 */
	case RMVJOB:
		rmvjob();
		rfstate = DONE;
		break;

	/*
	 *
	 * Command Processors Follow
	 *
	 */
	case RCVCMND:
		rcvcmnd(0);
		break;

	case RCVBYTE:
		rcvbyte(0);
		break;

	case RCVCFA:
		rcvcfA(0);
		break;

	case RCVDFA:
		rcvdfA(0);
		break;

	case LOADCMND:
		loadcmnd();
		break;

	case PJOBCMND:
		if (cmndbyte != -1) {
			/*
			 * Send ACK
			 */
			skwchar(lpdcskt, 0);
			skenque(lpdcskt, 1);
			/*
			 * Load cmndbyte plus 256 as rfstate
			 */
			rfstate = cmndbyte + 256;
			suspnd(-1);
		}
		break;

	case CFAEOF:
		ackseq |= ackbyte;

		if (!ackseq) { 
			cfAfh = rtopen(cfAfl,"r",0,0);
			getinfo(cfAfh);
			rtclose(cfAfh);
			if (!checkque(cfAinfo.que,cfAinfo.host,cfAinfo.user))
				ackseq = 1;
		}

		/*
		 * Send ACK / NAK
		 */
		skwchar(lpdcskt, ackseq);
		skenque(lpdcskt, 1);

		/*
		 * Delete file on error
		 */
		if (ackseq)
			delfil(0,cfAfl,0,0);

		rcvcmnd(PJOBCMND);
		break;
		
	case DFAEOF:
		ackseq |= ackbyte;

		/*
		 * Send ACK / NAK
		 */
		skwchar(lpdcskt, ackseq);
		skenque(lpdcskt, 1);

		/*
		 * Delete file on error
		 */
		if (ackseq)
			delfil(0,dfAfl,0,0);

		rcvcmnd(PJOBCMND);
		break;
		
	case DONE:
		rcnt = -1;
		break;

	default:
		break;

	}

	if((rcnt < 0) || (timeout > TIMEOUT)) {

#ifdef	DEBUGOPTION
if(debug&0x04) {
	printf("lpd(%d): rfstate = %d, rcnt = %d\r\n",
		code,rfstate,rcnt);
}
#endif
		rcnt = 0;

		/*
		 * Activate Printing
		 */
		lpdone();

		if(lpdcskt >= 0) {
			sksendwait(lpdcskt,(long) SENDWAIT);
			skclose(lpdcskt);
			lpdcskt = skrelease(lpdcskt,1);
		}
		if(rflag && !aflag) {
			/*
			 *  Start listening to the RLPD port
			 */
			while((lpdcskt = setlpd()) < 0 && !fndbrk) {
				/*
				 * Could not get a socket, wait 5 seconds
				 */
				suspnd(300);
			}
		} else {
			fndbrk = 1;
		}
	}
}

int
rcvbyte(i)
int i;
{
	/*
	 * Initialize for receiving a single byte
	 */
	if (i) {
		rfstate = RCVBYTE;
		retstate = i;
		rcvpos = 0;
		rcvstr[0] = '\0';
		ackbyte = 0;
		timeout = 0;
		return;
	}

	/*
	 * subroutine to wait for a single octet
	 */
	if (0 < (rcnt = skread(lpdcskt,&ackbyte,1))) {
		skdeque(lpdcskt);
		suspnd(-1);
		if (ackbyte)
			ackbyte = 1;

#ifdef	DEBUGOPTION
if(debug&0x04) {
	printf("rcvbyte(): byte = %d\r\n", ackbyte);
}
#endif

		rfstate = retstate;
		timeout = 0;
	}
}

int
rcvcmnd(i)
int i;
{
	register int icnt;

	/*
	 * Initialize for receiving command line
	 */
	if (i) {
		rfstate = RCVCMND;
		retstate = i;
		rcvpos = 0;
		rcvstr[0] = '\0';
		cmndbyte = -1;
		timeout = 0;
		return;
	}

	/*
	 * subroutine to wait for end of line
	 */
	icnt = 0;
	while(	0 < (rcnt = skread(lpdcskt,&rcvstr[rcvpos],1))) {
		icnt = 1;
		if(rcvstr[rcvpos] == '\n') {
			/*
			 * find end of string
			 */
			while (	(rcvstr[rcvpos] < 33) &&
				(rcvpos >= 0) )
				rcvpos--;
			/*
			 * put in terminator
			 */
			rcvstr[++rcvpos] = '\0';
			cmndbyte = rcvstr[0] & 0xFF;
			if (cmndbyte) {
				strcpy(rcvstr,rcvstr+1);
				rcvpos -= 1;
			}

#ifdef	DEBUGOPTION
if(debug&0x04) {
	printf("rcvcmnd(): command = %d,  str = %s\r\n", cmndbyte,rcvstr);
}
#endif

			rfstate = retstate;
			break;
		} else {
			if(rcvpos < BUFFERS-1)
				rcvpos += rcnt;
			rcvstr[rcvpos] = '\0';
		}
	}
	if (icnt) {
		skdeque(lpdcskt);
		suspnd(-1);
		timeout = 0;
	}
}

VOID
rcvcfA(i)
int i;
{
	register int icnt;
	char word[STRLEN];

	/*
	 * Initialize for receiving command file
	 */
	if (i) {
		/*
		 * Map the LPD cfA file to a local file
		 */
		getword(rcvstr,word);
		strcpy(cfAfl,lookup(word,"cfA")->tmpfile);

		rfstate = RCVCFA;
		retstate = i;
		rcvpos = 0;
		rcvstr[0] = '\0';
		rcvcnt = 0L;
		timeout = 0;
		ackseq = 0;

		if ((cfAfh = rtopen(cfAfl,"wn",0,0)) != NULL) {
			fprintf(cfAfh, "Q%s\r\n", quename);
		} else {
			ackseq = 1;
		}
		return;
	}

	/*
	 * subroutine to write out command file data
	 */
	icnt = 0;
	while(	0 < (rcnt = skread(lpdcskt,&rcvstr[rcvpos],1))) {
		icnt = 1;
		rcvcnt = rcvcnt + rcnt;
		if(rcvstr[rcvpos] == '\n') {
			/*
			 * find end of string
			 */
			while (	(rcvstr[rcvpos] < 33) && (rcvpos >= 0) )
				rcvpos--;
			rcvstr[++rcvpos] = '\0';
			/*
			 * Rewrite dfA file names with local names
			 */
			if (islower(rcvstr[0]) || isdigit(rcvstr[0]) ||
			   (rcvstr[0] == 'U')) {
				getword(rcvstr+1,word);
				strcpy(rcvstr+1,lookup(word,"dfA")->lclfile);
			}
			/*
			 * put in terminator
			 */
			strcpy(&rcvstr[strlen(rcvstr)],"\r\n");
			rcvpos = strlen(rcvstr);
			/*
			 * write data to file
			 */
			if ((cfAfh != NULL) &&
			    !fwrite(rcvstr,rcvpos,1,cfAfh)) {
				/*
				 * disk full err
				 */
				rtclose(cfAfh);
				cfAfh = NULL;
				ackseq = 1;
			}
			rcvpos = 0;
			rcvstr[0] = '\0';
			/*
			 * Close File at End of Data
			 */
			if (rcvcnt == cfAcnt) {
				if(cfAfh != NULL) {
					if(rtclose(cfAfh)) {
						ackseq = 1;
					}
					cfAfh = NULL;
				}
				rcvbyte(retstate);
				break;
			}
		} else {
			if(rcvpos < BUFFERS-1)
				rcvpos += rcnt;
			rcvstr[rcvpos] = '\0';
		}
	}
	if (icnt) {
		skdeque(lpdcskt);
		suspnd(-1);
		timeout = 0;
	}

#ifdef	DEBUGOPTION
if(debug&0x04) {
	printf("rcvcfA(): rcvcnt = %ld,  cfAcnt = %ld\r\n", rcvcnt, cfAcnt);
}
#endif

}

VOID
rcvdfA(i)
int i;
{
	register int icnt;
	char word[STRLEN];

	/*
	 * Initialize for receiving data file
	 */
	if (i) {
		/*
		 * Map the LPD dfA file to a local file
		 */
		getword(rcvstr,word);
		strcpy(dfAfl,lookup(word,"dfA")->tmpfile);

		rfstate = RCVDFA;
		retstate = i;
		rcvpos = 0;
		rcvstr[0] = '\0';
		rcvcnt = 0L;
		timeout = 0;
		ackseq = 0;

		if ((dfAfh = rtopen(dfAfl,"wn",0,0)) == NULL)
			ackseq = 1;
		return;
	}

	/*
	 * subroutine to write out data file data
	 */
	icnt = 0;
	while(	0 < (rcnt = skread(lpdcskt,&rcvstr[rcvpos],1))) {
		icnt = 1;
		rcvcnt = rcvcnt + rcnt;
		/*
		 * Build a Line of Text
		 */
		if (rcvstr[rcvpos] == '\n') {
			strcpy(&rcvstr[rcvpos],"\r\n");
			if ((dfAfh != NULL) &&
				!fwrite(rcvstr,rcvpos+2,1,dfAfh)) {
				/*
				 * disk full err
				 */
				rtclose(dfAfh);
				dfAfh = NULL;
				ackseq = 1;
			}
			rcvpos = 0;
		} else
		if (rcvpos == sizeof(rcvstr) - 3) {
			if ((dfAfh != NULL) &&
				!fwrite(rcvstr,rcvpos+1,1,dfAfh)) {
				/*
				 * disk full err
				 */
				rtclose(dfAfh);
				dfAfh = NULL;
				ackseq = 1;
			}
			rcvpos = 0;
		} else {
			rcvpos += 1;
		}
		/*
		 * Close File at End of Data
		 */
		if (rcvcnt == dfAcnt) {
			if (rcvpos) {
				strcpy(&rcvstr[rcvpos],"\r\n");
				if ((dfAfh != NULL) &&
					!fwrite(rcvstr,rcvpos+2,1,dfAfh)) {
					/*
					 * disk full err
					 */
					rtclose(dfAfh);
					dfAfh = NULL;
					ackseq = 1;
				}
				rcvpos = 0;
			}
			if(dfAfh != NULL) {
				if(rtclose(dfAfh)) {
					ackseq = 1;
				}
				dfAfh = NULL;
			}
			rcvbyte(retstate);
			break;
		}
	}

	if (icnt) {
		skdeque(lpdcskt);
		suspnd(-1);
		timeout = 0;
	}

#ifdef	DEBUGOPTION
if(debug&0x04) {
	printf("rcvdfA(): rcvcnt = %ld,  dfAcnt = %ld\r\n", rcvcnt, dfAcnt);
}
#endif

}

VOID
loadcmnd()
{
	register int i;

	if (cmndbyte == -1)
		return;

	strlwr(rcvstr);

	/*
	 * keep queue name
	 */
	getword(rcvstr,quename);

	/*
	 * Save any argumnents
	 */
	for (i=0; i<LISTSIZE; i++) {
		getword(rcvstr,list[i]);
	}

#ifdef	DEBUGOPTION
if(debug&0x04) {
	printf("Queue = %s\r\n", quename);
	for (i=0; (i<LISTSIZE) && (list[i][0] != '\0'); i++)
		printf("  List Element = %s\r\n", list[i]);
}
#endif

	if (checkque(quename,"*","*")) {
		/*
		 * Log Session
		 */
		sprintf(scratch,
	/*Log*/
	"RLPD: %s", quename
	/*Log*/
		);
		logsession(lpdcskt,scratch);
		/*
		 * Send ACK
		 */
		skwchar(lpdcskt, 0);
		/*
		 * Load cmndbyte as rfstate
		 */
		rfstate = cmndbyte;
	} else {
		/*
		 * Send NAK
		 */
		skwchar(lpdcskt, 1);
		/*
		 * Set rfstate as DONE
		 * and terminate connection
		 */
		rfstate = DONE;
	}
	skenque(lpdcskt, 1);
}

/*
 * getword: remove a word from a string.  Things within quotes are
 * assumed to be one word.
 * return TRUE on success, FALSE on end of string
 */

int getword(string,word)
char *string;
register char *word;
{
	register char *p;
	register int i;
	char *q;

	i = 0;

	/*
	 * skip leading blanks
	 */
	p = stpblk(string);
	if(!(*p)) {
		/*
		 * no words in string
		 */
		word[0] = '\0';
		return(FALSE);
	}
	if(*p == '\"') {
		/*
		 * word delimited by quotes
		 */
		while(p[++i] && p[i] != '\"')
			word[i-1] = p[i];
		word[i-1] = '\0';
		if(!p[i]) {
			/*
			 * Missing \". Assumed at end of string.
			 */
		} else {
			i++;
		}
		q = p+i;
	} else {
		/*
		 * get word, max len STRLEN
		 */
		q = stptok(p, word, STRLEN-1, " \t\r\n");
	}
     	/*
	 * remove trailing blanks
	 */
	p = stpblk(q);
	/*
	 * remove extracted stuff
	 */
	strcpy(string,p);
	return(TRUE);
}

char *stptok( p, toword, ilen, delim)
register char *p;
char *toword;
int ilen;
char *delim;
{
	register char *adv;
	register int i;
	int j,end;
 
	adv = toword;
	end = 0;
	j = strlen(delim);

 	do { 
 		for(i=0; i<j; i++)
 			if(*p == delim[i] || (!*p))
				end++;
 		if(!end) {
 			if(adv >= (toword+ilen-1)) 
				end++;
 			*adv++=*p++;
 		}
 	} while(!end);
 	*adv='\0';
 	return(p);
}
 
char *stpblk(ch)
register char *ch;
{
 	while(*ch == ' ' || *ch == '\t') ch++;
 	return(ch);
}

VOID strlwr(st)
register char *st;
{
	while(*st) {
		*st = tolower(*st);
		*st++;
	}
}

/*
 *  checkque (que,host,user)
 *
 *  Check the configuration file for a queue/host/user match.
 *  An inaccessable configuration file results in an invalid return.
 *  An unknown queue results in an invalid return.
 *
 *  Returns valid(1)/invalid(0)
 */
int checkque(que,host,user)
char *que,*host,*user;
{
	char word[STRLEN];

	if(NULL == (lpdcfh = rtopen(lpdcfl,"r",0,0))) {
#ifdef	DEBUGOPTION
if(debug&0x08) {
	printf("checkque(): no lpdcfl: %s\r\n", lpdcfl);
}
#endif
		return(0);
	}
	/*
	 * First line is sequence number
	 */
	fgetss(scratch,sizeof(scratch)-1,lpdcfh);
	getword(scratch,word);

#ifdef	DEBUGOPTION
if(debug&0x08) {
	printf("checkque(): sequence number = '%s'\r\n", word);
}
#endif
	/*
	 * Second line is temporary file directory
	 */
	fgetss(scratch,sizeof(scratch)-1,lpdcfh);
	getword(scratch,lpddir);

#ifdef	DEBUGOPTION
if(debug&0x08) {
	printf("checkque(): lpddir = '%s'\r\n", lpddir);
}
#endif
	/*
	 * Third line is password file directory
	 */
	fgetss(scratch,sizeof(scratch)-1,lpdcfh);
	getword(scratch,word);

#ifdef	DEBUGOPTION
if(debug&0x08) {
	printf("checkque(): paswrd = '%s'\r\n", word);
}
#endif
	/*
	 * Subsequent lines are queue lists
	 */
	while (NULL != fgetss(scratch,sizeof(scratch)-1,lpdcfh)) {

#ifdef	DEBUGOPTION
if(debug&0x08) {
	printf("checkque(): lpd = '%s'  '%s'  '%s'\r\n", que,host,user);
	printf("%s\r\n", scratch);
}
#endif
		/*
		 * Get queue/host/user information
		 */
		strlwr(scratch);
		getword(scratch,queinfo.que);
		getword(scratch,queinfo.host);
		getword(scratch,queinfo.user);

		if(streq(que,queinfo.que)) {
			if (streq("*",host) ||
			    streq("*",queinfo.host) ||
			    streq(host,queinfo.host)) {
				if (streq("*",user) ||
				    streq("*",queinfo.user) ||
				    streq(user,queinfo.user)) {
					/*
					 * Queue found
					 */
					rtclose(lpdcfh);
					return(1);
				}
			}
		}		
	}
	rtclose(lpdcfh);
	return(0);
}

/*
 * lookup
 *
 * Lookup the LPD cfA/dfA file name and return the structure pointer.
 * If the name is not found enter the name in the list
 * and create a new local name then return the new structure pointer.
 */

struct lstruct *
lookup(id,name)
char *id,*name;
{
	int seq;
	register struct lstruct *sp;

	/*
	 * Search list for a match
	 */
	sp = look;
	while (sp != NULL) {
		if (strneq(id,sp->lpdfile,sizeof(sp->lpdfile)))
			return(sp);
		sp = sp->next;
	}

	/*
	 * No match, create entry
	 */
	sp = (struct lstruct *) new (sizeof(struct lstruct));

	/*
	 * Link to list
	 */
	sp->next = look;
	look = sp;

	/*
	 * Fill in structure entries
	 */
	strncpy(sp->lpdfile,id,sizeof(sp->lpdfile));
	seq = lpdsequence();
	sprintf(sp->lclfile,"%-.6s.%03d",name,seq);
	sprintf(sp->tmpfile,"tmp%-.3s.%03d",name,seq);
	return(sp);
}

/*
 * new
 *
 * Allocate space
 * Abort if out of space
 */

VOID * new(n)
unsigned int n;
{
	register VOID *p;

	if ((p = (VOID *) malloc(n)) == NULL) {
		fprintf(stderr, "Out of space!\r\n");
		skclose(lpdcskt);
		skrelease(lpdcskt,1);
		exit(1);
	}

	return (p);

}

/*
 * lpdsequence
 *
 * Return the current RLPD sequence number
 * or 0 if the file could not be opened.
 */
static char lpdblock[512];	/* RLPD sequence block */

int lpdsequence()
{
	FILE *fp;
	char seqstr[8];
	int seqnum;

	/*
	 * The RLPD sequence file must be locked to ensure
	 * no other process has simultaneous access to
	 * the file while the file is being updated.
	 * If TSX+ is not sysgened for shared-file
	 * record locking then record locking does not occur.
	 */
	if((fp = rtopen(lpdcfl,"rn",0,0)) == NULL)
		return(0);
	frcdlock(0,fp);
	frcdread(&lpdblock,0,fp);
	seqnum = 0;
	seqstr[0] = '\0';
	strncat(seqstr,lpdblock,3);
	sscanf(seqstr,"%d",&seqnum);
	++seqnum;
	if ((seqnum <= 0) || (seqnum > 999))
		seqnum = 1;
	sprintf(seqstr,"%03d",seqnum);
	strncpy(lpdblock,seqstr,3);
	frcdwrite(&lpdblock,0,fp);
	frcdunlock(0,fp);
	rtclose(fp);
	return(seqnum);
}

/*
 * lpdone
 *
 * Rename any Data or Control Files
 * Free allocated space
 */

VOID
lpdone()
{
	register struct lstruct *sp;

	if(look != NULL) {
		/*
		 * Rename Data Files First
		 */
		sp = look;
		while (sp != NULL) {
			if (strneq("dfA",sp->lclfile,3)) {
				rename(sp->tmpfile,sp->lclfile,0,0);
			}
			sp = sp->next;
		}
		/*
		 * Rename Control Files Last
		 */
		sp = look;
		while (sp != NULL) {
			if (strneq("cfA",sp->lclfile,3)) {
				rename(sp->tmpfile,sp->lclfile,0,0);
			}
			sp = sp->next;
		}
		/*
		 * Free Space
		 */
		while (look != NULL) {
			sp = look;
			look = look->next;
			free(sp);
		}
		cl_xmit(TASKCLASS,STARTJOB,lprint);
	}
}

/*
 * getinfo
 *
 * Read queue/host/user information from a cfA file
 */

VOID getinfo(fp)
FILE *fp;
{
	register int i;
	char scr[STRLEN];

	cfAinfo.que[0]  = '\0';
	cfAinfo.host[0] = '\0';
	cfAinfo.user[0] = '\0';
	for (i=0; i<LISTSIZE; i++) {
		cfAinfo.src[i][0]  = '\0';
		cfAinfo.file[i][0] = '\0';
	}

	while ((fp != NULL) && (fgetss(scr,STRLEN-1,fp) != NULL)) {
		strlwr(scr+1);
		switch(scr[0]) {
		case C_QUEUE:
			strcpy(cfAinfo.que,scr+1);
			break;

		case C_HOST:
			strcpy(cfAinfo.host,scr+1);
			break;

		case C_USER:
			strcpy(cfAinfo.user,scr+1);
			break;

		case C_SOURCE:
			for (i=0; i<LISTSIZE; i++) {
				if(cfAinfo.src[i][0] == '\0') {
					strcpy(cfAinfo.src[i],scr+1);
					break;
				} else {
					if (streq(cfAinfo.src[i],scr+1))
						break;
				}
			}
			break;

		case C_RTROFF:
		case C_ITROFF:
		case C_BTROFF:
		case C_STROFF:
		case C_CIF:
		case C_DVI:
		case C_PRINT:
		case C_PLOT:
		case C_BUPLOT:
		case C_CONTROL:
		case C_DITROFF:
		case C_POSTSCRIPT:
		case C_PR:
		case C_FORTRAN:
		case C_TROFF:
		case C_RASTER:
		case C_PALLADIUM:
			for (i=0; i<LISTSIZE; i++) {
				if(cfAinfo.file[i][0] == '\0') {
					strcpy(cfAinfo.file[i],scr+1);
					break;
				} else {
					if (streq(cfAinfo.file[i],scr+1))
						break;
				}
			}
			break;

		case C_CLASS:
		case C_INDENT:
		case C_JOB:
		case C_BANNER:
		case C_MAIL:
		case C_SLINK:
		case C_TITLE:
		case C_ULINK:
		case C_WIDTH:
		default:
			break;
		}
	}
}

/*
 * questat
 *
 * Report RLPD Queue Status
 */

VOID questat()
{
	register char *p;
	int first,found;
	register int i,j;
	struct socket *skt;

	first = 0;
	skt = (struct socket *) mapskt(lpdcskt);

	lpdresp(
	/*NET*/
	skt->hostname
	/*NET*/
	);

/*1*/	if (!cd(lpddir) && ((p = firstname("cfA.*")) != NULL)) {

/*2*/	do {
		getinfo(filiop());

#ifdef	DEBUGOPTION
if(debug&0x10) {
	printf("cfA file   = %s\r\n", expand(scratch,p));
	printf("  Queue    = %s\r\n", cfAinfo.que);
	printf("  Host     = %s\r\n", cfAinfo.host);
	printf("  User     = %s\r\n", cfAinfo.user);
	for (i=0; i<LISTSIZE; i++) {
		if ((cfAinfo.src[i][0] != '\0') ||
		    (cfAinfo.file[i][0] != '\0')) {
			printf("    Source = %s\r\n", cfAinfo.src[i]);
			printf("    File   = %s\r\n", cfAinfo.file[i]);
		}
	}
}
#endif

		found = 0;
		if (streq(cfAinfo.que,quename)) {
		    if (list[0][0] == '\0') {
			found = 1;
		    } else {
			for (i=0; i<LISTSIZE; i++) {
			    if (list[i][0] != '\0') {
				for (j=0; j<LISTSIZE; j++) {
				    if ((cfAinfo.src[j][0] != '\0') ||
					(cfAinfo.file[j][0] != '\0')) {
					if (streq(cfAinfo.user,list[i]) ||
					    streq(cfAinfo.src[j],list[i]) ||
					    streq(cfAinfo.file[j],list[i])) {
					    found = 1;
					}
				    }
				}
			    }
			}
		    }
		}
		if (found) {
			if (!first) {
				first = 1;
	/*NET*/
/*
"         111111111122222222223333333333444444444455555555556666666666777777
"123456789012345678901234567890123456789012345678901234567890123456789012345
 */
				lpdresp(
"queue     host                job/user       source                   file"
				);
				lpdresp(
"-----     ----                --------       ------                   ----"
				);
	/*NET*/
			}
			for (i=0; i<LISTSIZE; i++) {
				if ((cfAinfo.src[i][0] != '\0') ||
				    (cfAinfo.file[i][0] != '\0')) {
					sprintf(scratch,
	/*NET*/
	"%-9.9s %-19.19s %-14.14s %-24.24s %-7.7s",
	cfAinfo.que,cfAinfo.host,cfAinfo.user,cfAinfo.src[i],cfAinfo.file[i]
	/*NET*/
					);
					lpdresp(scratch);
				}
			}
		}
/*2*/	} while ((p = nextname()) != NULL);

/*1*/	}

	if (!first) {
		lpdresp(
	/*NET*/
	"LPD Queue is Empty."
	/*NET*/
		);
	}

	lpdresp(
	/*NET*/
	""
	/*NET*/
	);
}

/*
 * rmvjob
 *
 * Delete RLPD Queued Files
 */

VOID rmvjob()
{
	register char *p;
	register int i,j;

/*1*/	if (!cd(lpddir) && ((p = firstname("cfA.*")) != NULL)) {

/*2*/	do {
	    getinfo(filiop());

#ifdef	DEBUGOPTION
if(debug&0x20) {
	printf("cfA file   = %s\r\n", expand(scratch,p));
	printf("  Queue    = %s\r\n", cfAinfo.que);
	printf("  Host     = %s\r\n", cfAinfo.host);
	printf("  User     = %s\r\n", cfAinfo.user);
	for (i=0; i<LISTSIZE; i++) {
		if ((cfAinfo.src[i][0] != '\0') ||
		    (cfAinfo.file[i][0] != '\0')) {
			printf("    Source = %s\r\n", cfAinfo.src[i]);
			printf("    File   = %s\r\n", cfAinfo.file[i]);
		}
	}
}
#endif

	    if (streq(cfAinfo.que,quename)) {
		if (streq(cfAinfo.user,list[0])) {
		    /*
		     * For a User, delete specified file(s)
		     */
		    for (i=1; i < LISTSIZE && list[i][0] != '\0'; i++) {
			for (j=0; j < LISTSIZE; j++) {
			    if (streq(cfAinfo.file[j],list[i]))
				delete(rtfile(cfAinfo.file[j],p));
			}
		    }
		} else
		if (streq("system",list[0])) {
		    /*
		     * For 'system' with 'user', delete all 'user' files
		     */
		    if (streq(cfAinfo.user,list[1])) {
			for (i=2; i < LISTSIZE && list[i][0] != '\0'; i++) {
			    for (j=0; j < LISTSIZE; j++) {
				if (cfAinfo.file[j][0] != '\0')
				    delete(rtfile(cfAinfo.file[j],p));
			    }
			}
		    /*
		     * For 'system' with file, delete specified file(s)
		     */
		    } else {
			for (i=1; i < LISTSIZE && list[i][0] != '\0'; i++) {
			    for (j=0; j < LISTSIZE; j++) {
				if (streq(cfAinfo.file[j],list[i]))
				    delete(rtfile(cfAinfo.file[j],p));
			    }
			}
		    }
		}
	    }
/*2*/	} while ((p = nextname()) != NULL);

/*1*/	}
}
                                                                                                                                                                                                                                                                                                                                         