/* ftpl.c */

#define FTPLMASTER

/*
 *  Includes
 */
#include <stdio.h>
#include <string.h>

#include "os.h"
#include "vrsion.h"
#include "vftpl.h"
#include "dfault.h"
#include "evtdef.h"
#include "hstdef.h"
#include "prodef.h"
#include "rtfile.h"
#include "ftppi.h"
#include "cliutl.h"
#include "kbdutl.h"
#include "inicli.h"
#include "cticks.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	int	ftpgets();	/* (str,lim,echo) read line from keyboard */
extern	VOID	ftpexit();	/* (i) exit from ftp */
extern	int	dumpcon();	/* dump connection to screen */
extern	VOID	filter();	/* (cnt) filter telnet options */
extern	int	question();	/* (cstr,clen,qstr) pose a question */
extern	int	ftpresp();	/* (s,len) response function for tsxutl */
extern	int	toggle();	/* (cstr,wstr,flag,on,off) toggle flags */
extern	VOID	ftppi();	/* (command) protocol interpreter */
extern	int	setmode();	/* (mode,filnam) set mode, return old mode */
extern	int	ftpopen();	/* (lpnum,dst,port) open FTP connection */
extern	int	ftpport();	/* prepare port */
extern	int	ftpcnct();	/* open FTP data connection */
extern	int	ftplstn();	/* listen for FTP data connection */
extern	int	ftpdo();	/* (s,filnam) do a command */
extern	int	ftpreplies();	/* (rcode) get response to server */
extern	int	ftpabort();	/* (how) abort FTP */
extern	int	rgetline();	/* get line from remote server */
extern	VOID	userftpd();	/* FTP recieve and send file functions */
extern	int	getword();	/* (s,word) remove word from string */
extern	int	finduniq();	/* (nam,lst,lstsz) find unique string */
extern	int	checkevent();	/* process server events */
extern	int	getnname();	/* (s,word) get next name from list */
extern	VOID	strlwr();	/* (s) convert string to lower case */
extern	VOID	printtxt();	/* (s) printout text */
#endif

extern	FILE *	opncap();	/* (filspc,mode) open the capture file */
extern	char *	rmtfile();	/* (s) convert to file spec string */
extern	char	*stptok();	/* (p,word,ilen,delim) stop on token */
extern	char	*stpblk();	/* (s) skip spaces and tabs */
extern	char *	filnam();	/* (s,p,wild) wild card lookup */
extern	char *	firstname();	/* (s) return first name */
extern	char *	nextname();	/* return next file name */
extern	char *	lastname();	/* terminate wild card lookup */

/*
 * Debugging options
 *
 *	bit 0	(0x01)	enable event printing
 *	bit 1	(0x02)	enable command printing
 *	bit 2	(0x04)	debug ftpdo()
 *	bit 3	(0x08)	debug ftpport()
 *	bit 4	(0x10)	debug ftpreplies()
 *	bit 5	(0x20)	debug rgetline()
 *	bit 6	(0x40)	debug userftpd()
 *	bit 7	(0x80)	debug getword()
 *	bit 8	(0x100)	debug getnname()
 */

#define DBUGTRUE	-1

static char *usetxt[] = {
       "",
#ifdef	DEBUGOPTION
       "   FTPL [?] [-d level] [-f filename] [-p filename]",
       "	   [-ghinrv] [destination host]",
       "	?		List this Help Text and Exit FTPL",
       "	d  level	Debug Level"
       "		1 -	enable event printing",
       "		2 -	enable command printing",
       "		4 -	enable ftpdo() printing",
       "		8 -	enable ftpport() printing",
       "	       16 -	enable ftpreplies printing",
       "	       32 -	enable rgetline() printing",
       "	       64 -	enable userftpd() printing",
       "	      128 -	enable getword() printing",
       "	      256 -	enable getnname() printing",
#else
       "   FTPL [?] [-f filename] [-p filename]",
       "	   [-ghinrv] [destination host]",
       "	?		List this Help Text and Exit FTPL",
#endif
       "	g		Wildcard Expansion Disabled",
       "	h		FTPL Help List",
       "	i		Interactive Prompting Off",
       "	n		AutoLogin Disabled on Connect",
       "	v		Verbose Mode Disabled",
       "",
	0
	};


#define	FASCII		 0
#define FIMAGE		 1

#define FALSE		 0
#define TRUE		 1
#define SUCCESS		 2
#define HAVEDATA	 4
#define ERROR	   	-1
#define NONE	   	-2
#define ABORT	   	-3
#define INCOMPLETE	-4
#define AMBIGUOUS	-5

static char			/* multiple file transfers */
	filename[16];
static char			/* file name */
	rmtname[16];
static char			/* capture file */
	*capfil = "wf:capfil.000";

static int
	xp = 0,			/* general pointer */
	towrite = 0,		/* file transfer pointer */
	len = 0,		/* file transfer length */
	ftpcskt = -1,		/* current command port */
	ftpdskt = -1,		/* current data port */
	ftpstate = 0,		/* state for background process */
	ftpfilemode = FASCII,	/* file open mode for transfer */
	finished = TRUE,	/* file transfer complete flag */
	tfrdly = 0,		/* transfer suspend time */
	tfrabtf = 0,		/* transfer abort flag */
	hash = 0,		/* hash mark printing */
	hashnum = 0,		/* number of hash marks printed */
	hashcnt = 0,		/* hash mark counter */
	linked = 0,		/* not connected */
	passive = 0,		/* to request passive mode or not */
	sendport = 1,		/* to send ports or not */
	verbose = 1,		/* informative messages */
	capture = FALSE,	/* capture list flag */
	bell = 0,		/* sound bell */
	autologin = 1,		/* login on connect */
	prompt = 1,		/* check on multiple commands */
	fromtty = TRUE,		/* default input from tty */
	wild = 1;		/* expand wildcards */

static	unsigned int
	iosize = 0;		/* transfer size in 512 byte blocks */

static long
	start,			/* transfer start time */
	tbytes;			/* number of bytes transferred */

static FILE *ftpfh = NULL;	/* file pointer for ftp transfers */
static FILE *capfp = NULL;	/* capture file for multiple commands */
static FILE *iop = NULL;	/* filnam utility */

/*
 * Transfer Delay Time Limits
 */

#define	MINTFRDLY	3
#define	MAXTFRDLY	15

/*
 * READSIZE is the system BLOCK SIZE
 * BUFFERS must be an integral multiple of READSIZE
 * xs[] must be at least 1 byte larger than READSIZE
 */

#define BUFFERS		1024	/* size of buffer */
#define READSIZE	512	/* how much to read */
#define	GBUFSIZE	200	/* general buffer size */

static char destname[50]={0,0};		/* who to connect to */
static char ftpcommand[GBUFSIZE];	/* command to execute */
static char printline[GBUFSIZE];	/* line to display */
static char s[GBUFSIZE];		/* line from other side */
static char xs[BUFFERS];		/* buffer space for file transfer */


/*
 * main - main procedure.  Displays opening message, parses arguments,
 *	  initializes network, reads user commands and executes them, and
 *	  cleans up.
 */

main(argc,argv)
int argc;
char *argv[];
{
	int c;
	register int i,j,k;

	/*
	 * Initialize for TSX+ clients
	 */
	inicli();

	/*
	 * Initialize kb handler
	 * Set the break character to ^C
	 */
	kb_init(0x03);

	/*
	 * identify self
	 */
	printtxt(vrstxt);

	/*
	 * initialize destination host name
	 */
	destname[0] = '\0';

	/*
	 * parse arguments
	 */
	for(i=1; i<argc; i++) {
		if(argv[i][0] == '?') {
		    printtxt(usetxt);
		    exit(0);
		} else
		if(argv[i][0] == '-') {
		    j = i;
		    k = 1;
		    while((c = argv[j][k]) != '\0') {
			switch(tolower(c)) {
			case 'v':	/* verbose mode disabled */
				verbose = FALSE;
				break;

			case 'n':	/* do not login on connect */
				autologin = FALSE;
				break;

			case 'i':	/* interactive prompting off */
				prompt = FALSE;
				break;

			case 'h':	/* help listing */
				ftppi("?");
				break;

			case 'g':	/* wildcard expansion off */
				wild = FALSE;
				break;

#ifdef	DEBUGOPTION
			case 'd':	/* debug, optional level */
				if(sscanf(argv[++i],"%d",&debug) <= 0)
					debug = TRUE;
				break;
#endif

			default:	/* unknown option */
				printf(
				"Unrecognized option -%c ignored\r\n", c);
				break;
			}
			k++;
		    }
		} else {
			/*
			 * destination host
			 */
			if(destname[0] != '\0')
				strncat(destname," ",49);
			strncat(destname,argv[i],49);
		}
	}

	/*
	 * Initialize various things,
	 */
	Snetinit();

	/*
	 * Create a unique file name for capture file
	 */
	sprintf(capfil,"wf:capfil.%03d", tljobn);

	if(destname[0])
		/*
		 * if destination specified, connect to it
		 */
		sprintf(ftpcommand,"open %s",destname);

	while(1) {
		if(*ftpcommand) {
			/*
			 * if command available, execute it
			 */
			ftppi(ftpcommand);
#ifdef	DEBUGOPTION
if(debug&0x02) {
			printf("after returning from ftppi()\r\n");
}
#endif
		}
		/*
		 * prompt
		 */
		kb_puts("ftp> ");
		/*
		 * read cmd from user
		 */
		ftpgets(ftpcommand,sizeof(ftpcommand),1);
	}
}

/*
 * General looping question routine
 * loops until a response is given
 *	returns 1 on ABORT
 *	   else 0
 */
static int question(cstr,clen,qstr)
char *cstr,*qstr;
int clen;
{
	while(!(*(stpblk(cstr)))) {
		/*
		 * get arg from user
		 */
		kb_puts(qstr);
		if(rtresp(cstr,clen))
			return(1);
	}
	return(0);
}

/*
 * Routine to get a keyboard response
 *	returns 1 on ABORT
 *	   else 0
 */
int rtresp(st,ln)
char *st;
int ln;
{
	return((ftpgets(st,ln,1)==ABORT) ? 1 : 0);
}

/*
 * tstbrk
 *
 * if fndbrk is set then:
 *	return(1)
 * else
 *	return(0)
 */
int tstbrk()
{
	if(fndbrk) {
		fndbrk = 0;
		return(1);
	} else {
		return(0);
	}
}

/*
 * ftpgets
 *
 * read a line from the keyboard
 * returns ABORT if keyboard input aborted
 * or non-zero on success
 */

int ftpgets(str,lim,echo)
register char *str;	/* where to put the line */
int lim,echo;		/* max chars to read, echo? */
{
	register char *save;
	register int c;

	save = str;		/* beginning of line */
	*save = '\0';

	if(tstbrk()) {
		kb_puts("^C\r\n");
		return(ABORT);
	}
	while(1) {
		/*
		 * build string from keyboard
		 */
		c = kb_gets(str,lim,echo);
		if(1 <= c && c <= 31) {
			kb_nline();
			return(strlen(save));
		}
		/*
		 * check for abort
		 */
		if(tstbrk()) {
			*save = '\0';
			kb_puts("^C\r\n");
			return(ABORT);	
		}
		/*
		 * check event queue
		 */
		checkevent();
		suspnd(0);
	}
}

/*
 * ftpexit
 *
 * exit from ftp
 */
static VOID ftpexit(i)
int i;
{
	if(i == ABORT) {
		kb_puts("\r\nFTPL session aborted by TCPIP\r\n");
	} else {
		while(linked && (checkevent() != ABORT)) {
			suspnd(0);
		}
	}

	/*
	 * Close all connections
	 */
	skclose(ftpcskt);
	skrelease(ftpcskt,1);
	skclose(ftpdskt);
	skrelease(ftpdskt,1);

	fclose(ftpfh);

	suspnd(0);
	errhandle();

	kb_puts("\r\nEscaping from FTPL\r\n");
	exit(0);
}

/*
 * dumpcon
 *
 * take everything from a connection and send it to the screen
 * return -1 on closed connection, else 0, 1 if paused
 */

static int dumpcon()
{
	register int cnt;

	/*
	 * get some from queue
	 */
	do {
		cnt = skread(ftpcskt,s,GBUFSIZE);
		if(cnt>0) {
			skdeque(ftpcskt);
			/*
			 * display on screen, etc.
			 */
			filter(cnt);
		}
	} while(cnt>0);
	/*
	 * 0 normally, -1 if connection closed
	 */
	return(cnt);
}


/*
 * filter
 *
 * filter telnet options on incoming data
 */

static VOID filter(cnt)
int cnt;
{
	register int i;

#ifdef	DEBUGOPTION
if(debug&0x100) {
	printf("filter(): cnt=%d \r\n",cnt);
}
#endif
	/*
	 * put on screen
	 */
	for(i=0; i<cnt; i++) {
		if(s[i] & 128) {
			/*
			 * if over ASCII 128 show as number
			 */
			kbprintf(" %d ", s[i]&0xFF);
		} else {
			kb_out(s[i]);
		}
	}

#ifdef	DEBUGOPTION
if(debug&0x100) {
	printf("filter(): leaving \r\n");
}
#endif
}

/*
 * The general toggle flag routine
 */
static int toggle(cstr,wstr,tflag,onstr,offstr)
char *cstr,*wstr,*onstr,*offstr;
register int *tflag;
{
	if(getword(cstr,wstr)) {
		strlwr(wstr);
		if(!strcmp(wstr,"off")) {
			*tflag = FALSE;
		} else
		if(!strcmp(wstr,"on")) {
			*tflag = TRUE;
		} else {
			*tflag = !*tflag;
		}
	} else {
		*tflag = !*tflag;
	}
	if(*tflag) {
		kb_puts(onstr);
	} else {
		kb_puts(offstr);
	}
	return(TRUE);
}

/*
 * FTP PI
 *
 * Protocol interpreter for user interface commands
 * Will permit any command to be abbreviated uniquely.
 * Recognizes commands, translates them to the protocol commands to be
 * sent to the other server, and uses userftpd to do data transfers.
 */

static int ftppi(command)
char *command;
{
	register int cmdno,i,j,curfilemode;
	int ivalue;
	char cmdname[50],cmndline[100];
	char word[50],answer[20];
	char *p,*q;

	/*
	 * print command
	 */
#ifdef	DEBUGOPTION
if(debug&0x02) {
	printf("command: %s\r\n",command);
}
#endif
	/*
	 * get command number
	 */
	if(!getword(command,cmdname))
		 return;
	/*
	 * removes first word from command
	 */
	strlwr(cmdname);
	cmdno = finduniq(cmdname,ftp_cmdlist,NCMDS);
	/*
	 * search cmdlist for prefix
	 */
	if(cmdno == AMBIGUOUS) {
		kb_puts("?Ambiguous command\r\n");
		return;
	}
	/*
	 * not a prefix of any command
	 */
	if(cmdno == NONE) {
		kb_puts("?-Invalid command\r\n");
		return;
	}
	/*
	 * process commands
	 */
	switch(cmdno) {
	case QMARK:
	case HELP:
		if(!command[0]) {
			kb_puts(
			"These valid commands may be abbreviated:\r\n");
			/*
			 * display command list
			 */
			printline[0] = '\0';
			for(i=0,j=0; i<NCMDS; i++) {
				/*
				 * skip if a blank entry
				 */
				if (ftp_cmdlst[i] == NULL)
					continue;
				/*
				 * get word from list
				 */
				if(j%5 != 4) {
					sprintf(word,"%-16s",ftp_cmdlist[i]);
					strcat(printline,word);
				} else {
					sprintf(word,"%s\r\n",ftp_cmdlist[i]);
					strcat(printline,word);
					kb_puts(printline);
					printline[0] = '\0';
				}
				j++;
			}
			/*
			 * last line
			 */
			if(printline[0] != '\0') {
				kb_puts(printline);
				kb_nline();
			}
		} else {
			kb_puts("Specific help not available\r\n");
		}
		return;

	case BELL:
		return(	toggle(command,word,&bell,
			"Bell mode on.\r\n",
			"Bell mode off.\r\n")
			);

	case BYE:
	case QUIT:
		if(linked) {
			ftpdo("QUIT");
			linked = 0;
		}
		/*
		 * exit FTP
		 */
		ftpexit(0);

#ifdef	DEBUGOPTION
	case DEBUG:	/* turn on/off debugging, optional level */
		if(sscanf(command,"%d",&ivalue)>0) {
			debug = ivalue;
			kbprintf("Debugging on(debug=%d).\r\n",debug);
		} else {
			toggle(command,word,&debug,
			"Debugging on.\r\n",
			"Debugging off.\r\n");
		}
		return;
#else
	case DEBUG:	/* no debugging */
		kb_puts("Debugging not available.\r\n");
		return;
#endif

	case WILD:	/* wildcard expansion */
		return(	toggle(command,word,&wild,
			"Wildcards enabled.\r\n",
			"Wildcards disabled.\r\n")
			);

	case HASH:	/* hash mark printing */
		return(	toggle(command,word,&hash,
			"Hash printing on(1024 bytes/hash mark).\r\n",
			"Hash printing off.\r\n")
			);

	case INTERACTIVE:	/* turn on interactive prompting */
	case NONINTERACTIVE:	/* turn off interactive prompting */
	case PROMPT:		/* interactive prompting */
		if(cmdno == INTERACTIVE)
			prompt = FALSE;		/* will be toggled */
		if(cmdno == NONINTERACTIVE)
			prompt = TRUE;		/* will be toggled */
		return(	toggle(command,word,&prompt,
			"Interactive mode on.\r\n",
			"Interactive mode off.\r\n")
			);

	case OPEN:		/* open connection to host */
		if(linked) {
			kb_puts("Already connected.\r\n");
			return;
		}
		if(question(command,100,"To: "))
			return;
		/*
		 * host name
		 */
		getword(command,destname);
		/*
		 *  Open a connection to the machine named.
		 */
		if(ftpopen(0,destname,HFTP) == FALSE)
			return;
		/*
		 * response from other end
		 */
		ftpreplies(&ivalue);
#ifdef	DEBUGOPTION
if(debug&0x10) {
		printf("return from ftpreplies()\r\n");
}
#endif
		if(tstbrk()) {
			return;
		}
		linked = TRUE;
		if(autologin) {
			strcpy(command,"user");
			ftppi(command);
		}
		return;

	case PASSIVE:		/* request PASSIVE mode for transfer */
		return(	toggle(command,word,&passive,
			"Passive mode enabled.\r\n",
			"Passive mode disabled.\r\n")
			);

	case SENDPORT:		/* send PORT commands for each transfer */
		return(	toggle(command,word,&sendport,
			"Use of PORT cmds on.\r\n",
			"Use of PORT cmds off.\r\n")
			);

	case STATUS:		/* display status info */
		if(linked) {
			kbprintf("Connected to %s\r\n",destname);
		}
		if(ftpfilemode == FASCII) {
			kb_puts("Transfer mode is ascii.\r\n");
		} else {
			kb_puts("Transfer mode is binary.\r\n");
		}
		if(bell) {
			kb_puts("Bell on.\r\n");
		} else {
			kb_puts("Bell off.\r\n");
		}
#ifdef	DEBUGOPTION
		if(debug) {
			kbprintf("Debugging on.(Debug=%d)\r\n",debug);
		} else {
			kb_puts("Debugging off.\r\n");
		}
#endif
		if(wild) {
			kb_puts("Filename wildcards enabled.\r\n");
		} else {
			kb_puts("Filename wildcards disabled.\r\n");
		}
		if(hash) {
			kb_puts("Hash-mark printing on.\r\n");
		} else {
			kb_puts("Hash-mark printing off.\r\n");
		}
		if(prompt) {
			kb_puts("Interactive prompting on.\r\n");
		} else {
			kb_puts("Interactive prompting off.\r\n");
		}
		if(passive) {
			kb_puts("Passive mode enabled.\r\n");
		} else {
			kb_puts("Passive mode disabled.\r\n");
		}
		if(sendport) {
			kb_puts("Sending of PORT commands on.\r\n");
		} else {
			kb_puts("Sending of PORT cmds off.\r\n");
		}
		if(verbose) {
			kb_puts("Verbose mode on.\r\n");
		} else {
			kb_puts("Verbose mode off.\r\n");
		}
		if(linked) {
			/*
			 * send STAT command
			 */
			kb_puts("\r\nRemote status:\r\n");
			i = verbose;
			verbose = TRUE;
			ftpdo("STAT");
			verbose = i;
		}
		return;

	case VERBOSE:		/* display informative messages */
		return(	toggle(command,word,&verbose,
			"Verbose mode on.\r\n",
			"Verbose mode off.\r\n")
			);

	case VERSION:		/* display current FTP version */
		printtxt(vrstxt);
		return;

	default:	/* The other commands valid only if connected */
		if(!linked) {
			kb_puts("Not connected.\r\n");
			return;
		}
		switch(cmdno) {
		case ASCII:		/* transfer mode */
			ftpdo("TYPE A");
			return;

		case BGET:		/* get file in binary mode */
			curfilemode = setmode(FIMAGE);
			if(question(command,100,"File: "))
				return;
			/*
			 * get file
			 */
			sprintf(cmndline,"RETR %s",command);
			ftpdo(cmndline);
			setmode(curfilemode);
			return;

		case BINARY:		/* binary mode */
			ftpdo("TYPE I");
			return;

		case BPUT:		/* put file in binary mode */
		case BSEND:		/* send file in binary mode */
			curfilemode = setmode(FIMAGE);
			/*
			 * if no arg, get from user
			 */
			if(question(command,100,"File: "))
				return;
			sprintf(cmndline,"STOR %s",command);
			ftpdo(cmndline);
			setmode(curfilemode);
			return;

		case CD:		/* change remote directory */
			/*
			 * if no arg, get from user
			 */
			if(question(command,100,"To: "))
				return;
			getword(command,word);
			/*
			 * special case
			 */
			if(!strcmp(word,"..")) {
				/*
				 * if CDUP understood
				 */
				if(ftpdo("CDUP") != ERROR)
					return;
				kb_puts("Trying again...\r\n");
				/*
				 * try alternative
				 */
				if(ftpdo("XCUP") != ERROR)
					return;
				kb_puts("Trying again...\r\n");
		 	}
			/*
			 * else try usual CWD
			 */
			sprintf(cmndline,"CWD %s",word);
			if(ftpdo(cmndline) != ERROR)
				return;
			kb_puts("Trying again...\r\n");
			/*
			 * else try XCWD
			 */
			sprintf(cmndline,"XCWD %s",word);
			ftpdo(cmndline);
			return;

		case CLOSE:		/* drop connection */
			ftpdo("QUIT");
			linked = FALSE;
			/*
			 * Close Connections
			 */
			skclose(ftpcskt);
			ftpcskt = skrelease(ftpcskt,1);

			skclose(ftpdskt);
			ftpdskt = skrelease(ftpdskt,1);

			fclose(ftpfh);
			ftpfh = NULL;

			return;

		case DEL:
		case RM:
			getword(command,word);
			if(question(word,100,"File: "))
				return;
			if(prompt) {
				/*
				 * check interactively
				 */
				kbprintf("Delete %s? ",word);
				rtresp(answer,sizeof(answer));
				if(tolower(*(stpblk(answer))) != 'y')
					return;
			}
			sprintf(cmndline,"DELE %s",word);
			ftpdo(cmndline);
			return;			
		
		case DIR:		/* get list of remote files */
		case LS:
			curfilemode = setmode(FASCII);
			sprintf(cmndline,cmdno==DIR ? "LIST" : "NLST");
			if(getword(command,word)) {
				sprintf(&cmndline[4]," %s",word);
			}
			ftpdo(cmndline);
			setmode(curfilemode);
			return;

		case GET:
		case RECV:		/* get remote file */
			if(question(command,100,"File: "))
				return;
			sprintf(cmndline,"RETR %s",command);
			ftpdo(cmndline);
			return;		

		case MDELETE:
			curfilemode = setmode(FASCII);
			if(question(command,100,"Files: "))
				return;
			/*
			 * for each arg
			 */
			while(getword(command,word) && !fndbrk) {
				if((capfp = opncap(capfil, "wn")) == NULL) {
					break;
				}
				if(wild) {
					capture = TRUE;
					sprintf(cmndline,"NLST %s",word);
					ftpdo(cmndline);
					capture = FALSE;
				} else {
					fprintf(capfp,"%s\n\r",word);
				}
				fclose(capfp);
				capfp = fopen(capfil, "rn");
				/*
				 * for each name
				 */
				while(getnname(word) && !fndbrk) {
				    if(prompt) {
					kbprintf("mdelete %s ? ",word);
					if(rtresp(answer,sizeof(answer))) {
						/*
						 * no more processing
						 */
						fndbrk = 1;
						break;
					}
					if(tolower(*(stpblk(answer))) != 'y')
						continue;
				    } else {
					kbprintf(
						"Deleting file %s\r\n", word);
				    }
				    sprintf(cmndline,"DELE %s",word);
				    if(ftpdo(cmndline) == ABORT) {
					fndbrk = 1;
					break;
				    }
				}
				if(capfp != NULL) {
					fclose(capfp);
					delete(capfil);
					capfp = NULL;
				}
			}
			setmode(curfilemode);
			return;

		case MGET:		/* get multiple files */
			if(question(command,100,"Files: "))
				return;
			/*
			 * for each arg
			 */
			while(getword(command,word) && !fndbrk) {
				if((capfp = opncap(capfil, "wn")) == NULL) {
					break;
				}
				if(wild) {
					curfilemode=setmode(FASCII);
					capture = TRUE;
					sprintf(cmndline,"NLST %s",word);
					ftpdo(cmndline);
					capture = FALSE;
					setmode(curfilemode);
				} else {
					fprintf(capfp,"%s\n\r",word);
				}
				fclose(capfp);
				capfp = fopen(capfil, "rn");
				/*
				 * for each name
				 */
				while(getnname(word) && !fndbrk) {
				    /*
				     * name may have special chars
				     */
				    q = rmtfile(word);
				    if(prompt) {
					kbprintf("%s as %s ? ", word,q);
					if(rtresp(answer,sizeof(answer))) {
						/*
						 * no more processing
						 */
						fndbrk = 1;
						break;
					}
					if(tolower(*(stpblk(answer))) != 'y')
						continue;
				    } else {
					kbprintf(
					    "getting %s as %s\r\n", word,q);
				    }
				    sprintf(cmndline,"RETR \"%s\"", word);
				    if(ftpdo(cmndline) == ABORT) {
					fndbrk = 1;
					break;
				    }
				}
				if(capfp != NULL) {
					fclose(capfp);
					delete(capfil);
					capfp = NULL;
				}
			}
			return;

		case MKDIR:		/* create directory */
			if(question(command,100,"Directory: "))
				return;
			/*
			 * try XMKD
			 */
			sprintf(cmndline,"XMKD %s",command);
			if(ftpdo(cmndline) != ERROR)
				return;
			kb_puts("Trying again...\r\n");
			/*
			 * else try MKD
			 */
			sprintf(cmndline,"MKD %s",command);
			ftpdo(cmndline);
			return;

		case MODE:		/* set stream mode */
			getword(command,word);
			strlwr(word);
			if(strncmp(word,"stream",strlen(word))) {
				kb_puts(
				"We only support stream mode, sorry.\r\n");
			} else {
				kb_puts("Mode is stream.\r\n");
			}
			return;

		case MPUT:		/* put multiple files */
		case MSEND:		/* send multiple files */
			if(question(command,100,"Files: "))
				return;
			/*
			 * for each arg
			 */
			while(getword(command,word) && !fndbrk) {
			/*
			 * no names expanded yet
			 */
			p = NULL;
			/*
			 * for each name
			 */
  			do {
			    /*
			     * verify file exists / get first matching file
			     */
			    if((p = filnam(word,p,wild)) == NULL) {
				continue;
			    }
			    if(prompt) {
				/*
				 * check
				 */
				kbprintf("%s ?", p);
				if(rtresp(answer,sizeof(answer))) {
					/*
					 * no more processing
					 */
					fndbrk = 1;
					break;
				}
				if(tolower(*(stpblk(answer))) != 'y')
					continue;
			    } else {
				kbprintf("sending file %s\r\n", p);
			    }
			    /*
			     * name may have special chars
			     */
			    sprintf(cmndline,
				"STOR %s \"%s\"", p, rmtfile(p));
			    if(ftpdo(cmndline) == ABORT) {
				fndbrk = 1;
				break;
			    }
  			} while(p != NULL && wild && !fndbrk);
			lastname();

			}
			return;

		case PUT:			
		case SEND:		/* put file */
			if(question(command,100,"File: "))
				return;
			/*
			 * put file
			 */
			sprintf(cmndline,"STOR %s",command);
			ftpdo(cmndline);
			return;

		case PWD:
			i = verbose;
			verbose = TRUE;
			/*
			 * try XPWD
			 */
			if(ftpdo("XPWD") != ERROR) {
				verbose = i;
				return;
			}
			kb_puts("Trying again...\r\n");
			/*
			 * else try PWD
			 */
			ftpdo("PWD");
			verbose = i;
			return;

		case QUOTE:
			if(question(command,100,"Command: "))
				return;
			/*
			 * send command
			*/
			ftpdo(command);
			return;
	
		case REMOTEHELP:	/* get help */
			i = verbose;
			verbose = TRUE;
			if(*(stpblk(command))) {
				/*
				 * for specific command
				 */
				sprintf(cmndline,"HELP %s",command);
				ftpdo(cmndline);
			} else {
				ftpdo("HELP");
			}
			verbose = i;
			return;

		case RENAME:		/* rename remote file */
			if(question(command,100,"From: "))
				return;
			getword(command,word);
			/*
			 * send rename from name
			 */
			sprintf(cmndline,"RNFR %s",word);
			if(ftpdo(cmndline) != INCOMPLETE)
				return;
			if(question(command,100,"To: ")) {
				ftpdo("ABOR");
				return;
			}
			/*
			 * send rename to name
			 */
			sprintf(cmndline,"RNTO %s",command);
			ftpdo(cmndline);
			return;
	
		case RMDIR:		/* remove remote dir */
			if(question(command,100,"Directory: "))
				return;
			/*
			 * try XRMD
			 */
			sprintf(cmndline,"XRMD %s",command);
			if(ftpdo(cmndline) != ERROR)
				return;
			kb_puts("Trying again...\r\n");
			/*
			 * try RMD
			 */
			sprintf(cmndline,"RMD %s",command);
			ftpdo(cmndline);
			return;
	
		case STRUCT:		/* set structure type - only file */
			getword(command,word);
			strlwr(word);
			if(strncmp(word,"file",strlen(word))) {
				kb_puts(
				"We only support file structure, sorry.\r\n");
			} else {
				kb_puts("Structure is file.\r\n");
			}
			return;
	
		case SYSTEM:		/* get remote system type */
			i = verbose;
			verbose = TRUE;
			ftpdo("SYST");
			verbose = i;
			return;
	
		case TYPE:		/* type a remote file */
			if(question(command,100,"File: "))
				return;
			getword(command,word);
			sprintf(cmndline,"RETR %s tt:tt.tt",word);
			ftpdo(cmndline);
			return;		

		case USER:		/* login to remote machine */
			if(question(command,100,"Username: "))
				return;
			/*
			 * username
			 */
			sprintf(cmndline,"USER %s",command);
			if(ftpdo(cmndline) == TRUE)
				return;
			/*
			 * password
			 */
			kb_puts("Password: ");
			if(ftpgets(command,100,0) == ABORT)
				return;
			sprintf(cmndline,"PASS %s",command);
			if(ftpdo(cmndline) == INCOMPLETE) {
				/*
				 * if account needed
				 */
				*command = '\0';
				if(question(command,100,"Account: "))
					return;
				sprintf(cmndline,"ACCT %s",command);
				ftpdo(cmndline);
			}
			return;
		
		default:	/* unknown command */
			printf(
			"***Program error: Unknown command no: %d\r\n",cmdno);
			break;
		}
	}
}

/*
 *  setmode
 *
 *  set the transfer mode
 *  and return old mode
 */
static int setmode(mode)
int mode;
{
	register int cmode;

	cmode = ftpfilemode;
	switch(mode) {
	default:
		kb_puts("Invalid ftpfilemode, defaulting to ASCII\r\n");

	case FASCII:
		if (cmode != FASCII)
			ftpdo("TYPE A");
		break;

	case FIMAGE:
		if (cmode != FIMAGE)
			ftpdo("TYPE I");
		break;

	}
	return(cmode);
}

/*
 *  ftpopen
 *
 *  open an FTP connection
 */
static int ftpopen(lpnum,dst,port)
int lpnum,port;
char *dst;
{
	register struct socket *skt;
	register int flag;
	int cl,ev,dat;

	/*
	 * Open a control connection
 	 */
	ftpcskt = addsess(lpnum,dst,port);
	if(ftpcskt >= 0) {
		flag = 1;
	} else {
		flag = 0;
	}
	while(flag && !tstbrk()) {
		if(Sgetevent(ABORTCLASS,&cl,&dat))
			ftpexit(ABORT);

		errhandle();
		ev = Sgetevent(CONCLASS,&cl,&dat);
		switch(ev) {
		case CONOPEN:
			/*
			 * Log the Session
			 */
			sprintf(printline,"FTPL: %s", destname);
			logsession(dat,printline);
			return(TRUE);

		case CONCLOSE:
		case CONFAIL:
			flag = 0;
			break;

		default:
			if(ev) {
				Stmrset(cl,ev,dat,1);
			} else {
				suspnd(0);
			}
		}
	}
	/*
	 * error on open
	 */
	skclose(ftpcskt);
	ftpcskt = skrelease(ftpcskt,1);
	skclose(ftpdskt);
	ftpdskt = skrelease(ftpdskt,1);
	errhandle();
	kbprintf("Unable to connect to %s\r\n",dst);
	return(FALSE);
}

/*
 * ftpport
 *
 * Open a connection (PASV mode) or setup a
 * Listen connection (ACTV mode).
 *
 * Returns socket number
 * or -1 on error.
 */
static int ftpport()
{
	int sknum;

	/*
	 * If PASV mode is enabled then get port number
	 * from Server and open connection.
	 */
	if(passive) {
		sknum = ftpcnct();
	} else {
		sknum = -1;
	}
	/*
	 * If PASV mode is not selected
	 * or refused by server then allow
	 * server to do an ACTIVE open.
	 */
	if(sknum == -1) {
		sknum = ftplstn();
	}
#ifdef	DEBUGOPTION
if(debug&0x08) {
	printf("ftpport(): sknum = %d\r\n", sknum);
}
#endif
	/*
	 * socket number
	 */
	if(sknum == -1) {
		kb_puts("FTP Data Connection Failed to Open\r\n");
	}
	return(sknum);
}

/*
 * ftpcnct
 *
 * Client Opens Connections
 */
static int ftpcnct()
{
	register struct socket *skt;
	register char *p;
	char st[24];
	int i,rcode,sknum;
	int ip[4],port[2];
	long stime;

	/*
	 * send PASV command
	 */
	skwrite(ftpcskt,"PASV\r\n",6);
	skenque(ftpcskt,1);
	/*
	 * get response
	 */
#ifdef	DEBUGOPTION
if(debug&0x08) {
	printf("ftplstn(): before ftpreplies() call \r\n");
}
#endif
	i = ftpreplies(&rcode);
#ifdef	DEBUGOPTION
if(debug&(0x08|0x10)) {
	printf("return from ftpreplies()\r\n");
}
#endif
	if(i != TRUE || rcode != 227) {
		return(-1);
	}
	/*
	 * Scan upto PORT Data
	 */
	p = s+4;
	while(*p < '0' || *p > '9') {
		if(*p++ == 0) {
			return(-1);
		}
	}
	/*
	 * full port number
	 */
	if(sscanf(	p,
			"%d,%d,%d,%d,%d,%d",
			ip+0,ip+1,ip+2,ip+3,
			port+0,port+1
							) != 6) {
		return(-1);
	}
	/*
	 * Create socket for data connection
	 */
	sknum = socket(1);
	if(sknum == -1)
		return(-1);

	/*
	 * Connecting to
	 */
	sprintf(	st,
			"%d.%d.%d.%d #%u",
			ip[0], ip[1], ip[2], ip[3],
			(port[0]<<8) + port[1]
		);
#ifdef	DEBUGOPTION
if(debug&0x02) {
	printf("%s\r\n",st);
}
#endif
	connect(sknum,st);
	/*
	 * Wait for connection to open
	 */
	stime = cticks(NULL);
	while(elapsed(stime) < 300L) {
		if(!sktest(sknum)){
			return(sknum);
		}
		suspnd(0);
	}
	skrelease(sknum,1);
	return(-1);
}

/*
 * ftplstn
 *
 * Server Opens Connections
 */
static int ftplstn()
{
	register struct socket *skt;
	register char *myip;
	int port,rcode,sknum;

	/*
	 * Create socket for data connection
	 */
	sknum = socket(1);
	if(sknum == -1)
		return(-1);
	/*
	 * setup the appropriate listening port
	 * the default port HFTP-1 should be used
	 * only for single user mode
	 * TCPIP will select a unique port if the
	 * default port (skt->sktport) is zero,
	 * else the specified port will be used.
	 * return the socket number if ok,
	 * else return -1 on error.
	 */
	if(!sendport) {
		listen(sknum,HFTP-1);
		return(sknum);
	}
#ifdef	DEBUGOPTION
if(debug&0x08) {
	printf("ftplstn(): before dumpcon() call \r\n");
}
#endif
	dumpcon();
	/*
	 * Setup listener
	 */
	port = listen(sknum,0);
	/*
	 * get my ip number
	 */
	skt = (struct socket *) mapskt(sknum);
	myip = skt->tcpout.i.ipsource;
	/*
	 * full port number
	 */
	sprintf(printline,"PORT %d,%d,%d,%d,%d,%d\r\n",
		*(myip+0) & 0xFF, *(myip+1) & 0xFF,
		*(myip+2) & 0xFF, *(myip+3) & 0xFF,
		(port>>8) & 0xFF,      port & 0xFF);
#ifdef	DEBUGOPTION
if(debug&0x02) {
		printf(printline);
}
#endif
	/*
	 * send PORT command
	 */
	skwrite(ftpcskt,printline,strlen(printline));
	skenque(ftpcskt,1);
	/*
	 * get response
	 */
#ifdef	DEBUGOPTION
if(debug&0x08) {
	printf("ftplstn(): before ftpreplies() call \r\n");
}
#endif
	rcode = ftpreplies(&rcode);
#ifdef	DEBUGOPTION
if(debug&(0x08|0x10)) {
	printf("return from ftpreplies()\r\n");
}
#endif
	/*
	 * socket number
	 */
	if(rcode == TRUE) {
		return(sknum);
	} else {
		skrelease(sknum,1);
		return(-1);
	}
}

/*
 * ftpdo
 *
 * Do whatever command is sent from the user interface using
 * userftpd, the background file handler
 * Returns code from ftpreplies
 */

static int ftpdo(str)
register char *str;
{
	register int i;
	int rcode;
	char name[50],name2[50];

	/*
	 * command to upper case
	 */
	for(i=0; i<4; i++) {
		str[i] = toupper(str[i]);
	}
	/*
	 * put file
	 */
	if(!strncmp(str,"STOR",4)) {
		/*
		 * first arg - local file
		 */
		getword(&str[5],name);
		/*
		 * check for second argument
		 */
		if(!str[5]) {
			/*
			 * if only one argument
			 */
			strcpy(&str[5],rmtfile(name));
		} else {
			/*
			 * second arg - removes quotes etc.
			 */
			getword(&str[5],name2);
			/*
			 * copy back into command
			 */
			strcpy(&str[5],name2);
		}
		/*
		 * open local file
		 */
		ftpfh = fopen(name,"rn");
		if(ftpfh == NULL) {
			kb_puts(" Cannot open file to transfer.\r\n");
			return(-1);
		}
		/*
		 * open data connection
		 */
#ifdef	DEBUGOPTION
if(debug&0x04) {
		printf("ftpdo(STOR): before calling ftpport()\r\n");
}
#endif
		ftpdskt = ftpport();
		if(ftpdskt == -1) {
			fclose(ftpfh);
			ftpfh = NULL;
			return(-1);
		}
		iosize = ftpfh->io_size;
		ftpstate = 20;
#ifdef	DEBUGOPTION
if(debug&0x04) {
		printf("ftpdo(STOR): after calling ftpport()\r\n");
}
#endif
	} else
	if(!strncmp(str,"RETR",4)) {
		/*
		 * get remote file
		 */
		getword(&str[5],name);
		if(!str[5]) {
			/*
			 * if only one argument
			 */
			/*
			 * strip name of device and subdirectories
			 */
			strcpy(name2,rmtfile(name));
	  	} else {
			/*
			 * two args present
			 */
			getword(&str[5],name2);
		}
		/*
		 * open local file
		 */
		ftpfh = fopen(name2,"wn");
		if(ftpfh == -1)
			return(-1);
		if(ftpfh == NULL) {
			kbprintf("Cannot open file to receive: %s\r\n",name);
			return(-1);
		}
		/*
		 * put remote name back into command
		 */
		strcpy(&str[5],name);
		/*
		 * open data connection
		 */
#ifdef	DEBUGOPTION
if(debug&0x04) {
		printf("ftpdo(RETR): before calling ftpport()\r\n");
}
#endif
		ftpdskt = ftpport();
		if(ftpdskt == -1) {
			fclose(ftpfh);
			ftpfh = NULL;
			delete(name2);
			return(-1);
		}
		ftpstate = 30;
#ifdef	DEBUGOPTION
if(debug&0x04) {
		printf("ftpdo(RETR): after calling ftpport()\r\n");
}
#endif
	} else
	if(!strncmp(str,"LIST",4) || !strncmp(str,"NLST",4)) {
		/*
		 * data connection
		 */
#ifdef	DEBUGOPTION
if(debug&0x04) {
		printf("ftpdo(LIST/NLST): before calling ftpport()\r\n");
}
#endif
		ftpdskt = ftpport();
		if(ftpdskt == -1)
			return(-1);
		ftpstate = 40;
#ifdef	DEBUGOPTION
if(debug&0x04) {
		printf("ftpdo(LIST/NLST): after calling ftpport()\r\n");
}
#endif
	} else
	if(!strncmp(str,"TYPE",4)) {
		if(toupper(str[5]) == 'I') {
			/*
			 * remember mode
			 */
			ftpfilemode = FIMAGE;
		} else
		if(toupper(str[5]) == 'A') {
			ftpfilemode = FASCII;
		}
	}

	/*
	 * clear command connection
	 */
	dumpcon();
	/*
	 * send command
	 */
	skwrite(ftpcskt,str,strlen(str));
	/*
	 * <CRLF> terminates command
	 */
	skwrite(ftpcskt,"\r\n",2);
	skenque(ftpcskt,1);

#ifdef	DEBUGOPTION
if(debug&0x02) {
	/*
	 * show command sent
	 */
	printf("---> %s\r\n",str);
}
#endif
	/*
	 * get remote response
	 */
#ifdef	DEBUGOPTION
if(debug&0x04) {
	printf("ftpdo(): before ftpreplies() call\r\n");
}
#endif
	i = ftpreplies(&rcode);
#ifdef	DEBUGOPTION
if(debug&(0x04|0x10)) {
	printf("return from ftpreplies()\r\n");
}
#endif
	if((i == NONE) && strncmp(str,"QUIT",4)) {
		/*
		 * unexpected connection drop
		 */
		kb_puts("lost connection\r\n");
		linked = FALSE;
	}
	if(i == ABORT || i == NONE || i == ERROR)  {
		/*
		 * if error, no transfer
		 */
		skclose(ftpdskt);
		ftpdskt = skrelease(ftpdskt,1);
		ftpstate = 0;
		fclose(ftpfh);
		ftpfh = NULL;
		/*
		 * dump any remaining comments
		 */
		dumpcon();
	}
	return(i);
}

/*
 * ftpreplies
 *
 * get responses to commands to server
 * return TRUE on successful completion,
 * INCOMPLETE if more commands needed for operation,
 * NONE on lost connection, ABORT on an abort, and ERROR on failure
 * PRELIMINARY and TRANSIENT responses wait for completion
 */
static int ftpreplies(rcode)
int *rcode;
{
	int abrtcnt,digit;
	register int cnt,ev,j;

#ifdef	DEBUGOPTION
if(debug&0x10) {
	printf("entering ftpreplies()\r\n");
}
#endif
	abrtcnt = 0;
	tfrabtf = 0;
	j = 0;
loop:	while(1) {
		/*
		 * get line from remote host
		 */
		cnt = rgetline();
#ifdef	DEBUGOPTION
if(debug&0x10 && !(debug&0x20)) {
		printf("cnt=rgetline(): =%d\r\n  s=%s\r\n",cnt,s);
}
#endif
		if(cnt == NONE)
			/*
			 * lost connection
			 */
			return(NONE);
		if(cnt == ABORT) {
			/*
			 * user abort
			 */
			if(ftpabort(&abrtcnt) == ABORT)
				return(ABORT);
			goto loop;
		}
		if(!sscanf(s,"%d",rcode))
			/*
			 * continuation line
			 */
			*rcode = -1;
		/*
		 * informative/error message or display on
		 */
		if(verbose || (*rcode == -1) || (*rcode >= 500))
			filter(cnt);

		if((*rcode/100) == 2) {
			/*
			 * wait till transfers complete
			 */
			while(ftpdskt >= 0) {
				ev = checkevent();
#ifdef	DEBUGOPTION
if(debug&0x10) {
				printf("checkevent()=%d\r\n",ev);
}
#endif
				if(ev == NONE) {
					/*
					 * lost connection
					 */
					return(NONE);
				}
				if(ev == ABORT) {
					/*
					 * user abort
					 */
					if(ftpabort(&abrtcnt) == ABORT)
						return(ABORT);
				}
				/*
				 * wait a while
				 */
				suspnd(6);
			}
		}
#ifdef	DEBUGOPTION
if(debug&0x10) {
		printf("*rcode=%d\r\n", *rcode);
}
#endif
		if(s[3] == '-') {
			/*
			 * line with continuations
			 * remember end code
			 */
			j = *rcode;
			continue;
		} else
		if(j) {
			if(*rcode == j) {
				/*
				 * end of continuation
				 */
				j = 0;
			} else {
				continue;
			}
		}
		/*
		 * first digit
		 */
		digit = (int)(*rcode/100);
		if(abrtcnt) {
			switch(digit) {
			case 1:		/* preliminary */
			case 4:		/* transient negative completion */
				continue;
	
			default:
				kb_puts(
				"Server response not understood.\r\n");
				kb_puts("Terminating command\r\n");

			case 2:		/* positive completion */
			case 3:		/* intermediate */
			case 5:		/* Permanent negative completion */
				return(ABORT);

			}
		} else {
			switch(digit) {
			case 1:		/* preliminary */
			case 4:		/* transient negative completion */
				continue;
	
			case 2:		/* positive completion */
				return(TRUE);

			case 3:		/* intermediate */
				return(INCOMPLETE);
		
			default:
				kb_puts(
				"Server response not understood.\r\n");
				kb_puts("Terminating command\r\n");

			case 5:		/* Permanent negative completion */
				return(ERROR);

			}
		}
	}
}

/*
 * ftpabort
 *
 * attempt to abort FTP operation
 * return ABORT on unconditional abort
 */
static int ftpabort(i)
int *i;
{
	finished = TRUE;
	switch(ftpstate) {
		default:	/* Invalid states */
		case 0:
			break;

		case 20:	/* finished == TRUE */
		case 21:	/* will terminate transfers */
		case 22:
			break;

		case 30:
		case 31:
		case 40:
		case 41:
			/*
			 * Send the abort as an urgent message
			 */
			sksendwait(ftpcskt,(long) SENDWAIT);
			skwurgent(ftpcskt,1);
			/* IAC IP IAC DM */
			skwrite(ftpcskt,"\377\364\377\362",4);
			skwrite(ftpcskt,"ABOR\r\n",6);
			skenque(ftpcskt,1);
			sksendwait(ftpcskt,(long) SENDWAIT);
			skwurgent(ftpcskt,0);
			break;
	}
	*i += 1;
	if(*i > 2) {
		kb_puts("\r\nUnconditonal ABORT\r\n");
		return(ABORT);
	} else {
		kb_puts("\r\nAttempting to ABORT operation ...\r\n");
		return(0);
	}
}

/*
 * rgetline - get a line from remote server
 *
 * return ABORT on an abort, NONE on lost connection,
 * length of received line on success
 */

static int rgetline()
{
	register int i,icnt,cnt,ev;

#ifdef	DEBUGOPTION
if(debug&0x20) {
	printf("entering rgetline()\r\n");
}
#endif
	i = 0;
	while(1) {
		ev = checkevent();
#ifdef	DEBUGOPTION
if(debug&0x20) {
		printf("checkevent()=%d\r\n", ev);
}
#endif
		switch(ev) {
		case NONE:	/* lost connection */
			skdeque(ftpcskt);

		case ABORT:	/* abort */
#ifdef	DEBUGOPTION
if(debug&0x20) {
			printf("leaving rgetline()\r\n");
}
#endif
			return(ev);

		case HAVEDATA:
			/*
			 * get some from queue
			 */
			icnt = 0;
			while(1) {
				cnt = skread(ftpcskt,&s[i],1);
				if(cnt == 0)
					/*
					 * nothing available
					 */
					break;
				icnt = 1;
				if((s[i++] == '\n') || (cnt < 0)) {
					/*
					 * end of line
					 */
					s[i] = '\0';
#ifdef	DEBUGOPTION
if(debug&0x20) {
					printf(
			"i=%d, s=%s\r\nleaving rgetline()\r\n", i,s);
}
#endif
					skdeque(ftpcskt);
					/*
					 * return line length
					 */
					return(i);
				}
				if(i > GBUFSIZE - 2)
					i = GBUFSIZE - 2;
			}
			if (icnt)
				skdeque(ftpcskt);
			break;

		default:	/* ignore other events */
			suspnd(6);
			break;
		}
	}
}

/*
 * userftpd
 *
 *  FTP receive and send file functions
 */

static VOID userftpd()
{
	register char *p;
	register char *q;
	register int fcnt;
	int ei,ef,i,ri,rf;
	long etime;

#ifdef	DEBUGOPTION
if(debug&0x40) {
	if(ftpstate) {
		printf("userftpd(%d):", ftpstate);
	}
}
#endif
	fcnt = 0;
	switch(ftpstate) {
	default:	/* unknown */
		break;

	case 40:	/* start LIST */
		if(sktest(ftpdskt))
			break;
		/*
		 * Initialize parameters
		 */
		tbytes = 0L;
		/*
		 * connection made
		 */
		start = cticks(NULL);
		ftpstate = 41;

	case 41:	/* get started */
		fcnt = skread(ftpdskt,xs,READSIZE);
		if(fcnt>0) {
			/*
			 * how much
			 */
			tbytes += fcnt;

			skdeque(ftpdskt);
			if(capture == TRUE) {
				if(!fwrite(xs,1,fcnt,capfp)) {
					tfrabtf = 1;
				}
			} else {
				xs[fcnt] = '\0';
				kb_puts(xs);
			}
		}
#ifdef	DEBUGOPTION
if(debug&0x40)	{
		printf(" fcnt %d tbytes %ld\r\n",fcnt,tbytes);
}
#endif
		break;

	case 30:	/* receive */
		if(sktest(ftpdskt))
			break;
		/*
		 * Initialize parameters
		 */
		len = 0;
		xp = 0;
		tfrdly = MINTFRDLY;
		hashcnt = 0;
		hashnum = 0;
		tbytes = 0L;
		/*
		 * connection made
		 */
		start = cticks(NULL);
		ftpstate = 31;

	case 31:
		/*
		 * file has already been opened,
		 * take everything from the connection
		 * and place into the open file: ftpfh
		 */
		/*
		 * the data transfer process is a cooperative
		 * task performed by this program and TCPIP.
		 * this test sequence attempts to maximize the
		 * the transfer rate by adjusting the process
		 * suspension time during the data transfer
		 */
		if(skqlen(ftpdskt) < LOWWATER) {
			if(tfrdly < MAXTFRDLY) tfrdly++;
		} else {
			if(tfrdly > MINTFRDLY) --tfrdly;
		}

		/*
		 * wait until xs is full
		 * before writing to disk
		 */
		if(len <= 0) {
			if(xp) {
			    if(ftpfh != NULL)
				if(!fwrite(xs,1,xp,ftpfh)) {
				    tfrabtf = 1;
				}
			    xp = 0;
			}
			/*
			 * expected or desired len to go
			 */
			len = BUFFERS;
		}
		/*
		 * how much to read
		 */
		if(len < BUFFERS) {
			i = len;
		} else {
			i = BUFFERS;
		}
		fcnt = skread(ftpdskt,&xs[xp],i);
#ifdef	DEBUGOPTION
if(debug&0x40) {
		printf(" len %d xp %d fcnt %d", len,xp,fcnt);
}
#endif
		/*
		 * adjust counts
		 */
		if(fcnt>0) {
			len -= fcnt;
			xp += fcnt;
			hashcnt += fcnt;
			tbytes += fcnt;
			skdeque(ftpdskt);
			if(len != 0) {
				suspnd(tfrdly);
			}
			suspnd(-1);
		}
		/*
		 * connection closed
		 */
		if(fcnt<0) {
			/*
			 * write last block
			 */
			if(xp) {
			    if(ftpfh != NULL)
				if(!fwrite(xs,1,xp,ftpfh)) {
				    tfrabtf = 1;
				}
			    xp = 0;
			}
		}
		/*
		 * hash mark printing
		 */
		while(hash && hashcnt >= HASHSIZE) {
			hashcnt -= HASHSIZE;
			if(++hashnum >= 75) {
				kb_puts("#\r\n");
				hashnum = 0;
			} else {
				kb_out('#');
			}
		}
		break;

	case 20:	/* send */
		if(sktest(ftpdskt))
			break;
		/*
		 * Initialize parameters
		 */
		finished = FALSE;
		towrite = 0;
		xp = 0;
		tfrdly = MINTFRDLY;
		hashcnt = 0;
		hashnum = 0;
		tbytes = 0L;
		/*
		 * connection made
		 */
		start = cticks(NULL);
		ftpstate = 21;

	case 21:
		/*
		 * the data transfer process is a cooperative
		 * task performed by this program and TCPIP.
		 * this test sequence attempts to maximize the
		 * the transfer rate by adjusting the process
		 * suspension time during the data transfer
		 */
		if(skqued(ftpdskt)) {
			if(tfrdly<MAXTFRDLY) tfrdly++;
		} else {
			if(tfrdly>MINTFRDLY) --tfrdly;
		}

		/*
		 * transfer file(s) to the
		 * other host via ftp request
		 * file is already open=ftpfh
		 */
		if(towrite <= xp && finished == FALSE) {
			/*
			 * need to read again
			 */
			for(p=xs,i=0; i<BUFFERS; i+=READSIZE) {
			    if((iosize != 0) &&
				(fcnt = fread(p,1,READSIZE,ftpfh))) {
				p += fcnt;
				--iosize;
			    } else {
				finished = TRUE;
				fclose(ftpfh);
				ftpfh = NULL;
				break;
			    }
			}
			fcnt = p - xs;
			if(ftpfilemode == FASCII) {
			    for (p=q=xs,i=0; i<fcnt; i++) {
				if (!(*p++ = *q++)) {
				    --p;
				}
			    }
			    fcnt = p - xs;
			}
			towrite = fcnt;
			xp = 0;
		}
		fcnt = towrite - xp;
		if(fcnt>0) {
			fcnt = skwrite(ftpdskt,&xs[xp],fcnt);
		}
		if(fcnt>0) {
			/*
			 * write successful
			 * adjust counts and queue data
			 */
			xp += fcnt;
			hashcnt += fcnt;
			tbytes += fcnt;
			skenque(ftpdskt,0);
		}
#ifdef	DEBUGOPTION
if(debug&0x40) {
		printf(" finished %d fcnt %d xp %d towrite %d",
			finished,fcnt,xp,towrite);
}
#endif
		/*
		 * hash printing
		 */
		while(hash && hashcnt >= HASHSIZE) {
			hashcnt -= HASHSIZE;
			if(++hashnum >= 75) {
				kb_puts("#\r\n");
				hashnum = 0;
			} else {
				kb_out('#');
			}
		}
		/*
		 * done if:  the file
		 * is all read from disk
		 * and all sent or other side
		 * has ruined connection
		 */
		if((towrite <= xp && finished == TRUE) || sktest(ftpdskt)) {
			ftpstate = 22;
		} else {
			if(skqued(ftpdskt)) {
				suspnd(tfrdly);
			}
			suspnd(-1);
			break;
		}

	case 22:
		/* send done
		 * wait for other side to accept
		 * everything and then start close
		 */
		if(0 >= (i = skenque(ftpdskt,1)) || sktest(ftpdskt)) {
			fcnt = -1;
		}
#ifdef	DEBUGOPTION
if(debug&0x40) {
		printf(" skenque = %d", i);
}
#endif
		break;
	}
#ifdef	DEBUGOPTION
if(debug&0x40) {
	if(ftpstate) {
		printf("\r\n");
	}
}
#endif
	/*
	 * after processing from connection,
	 * if the connection is closed,
	 * reset up shop.
	 */
	if(fcnt < 0) {
		/*
		 * transfer finished / connection lost
		 */
		if(ftpfh != NULL) {
			/*
			 * close file
			 */
			if(fclose(ftpfh)) {
				tfrabtf = 1;
			}
			ftpfh = NULL;
		}
		/*
		 * Hash Mark Printing
		 */
		if(hash && hashnum)
			kb_nline();
		/*
		 * check transfer abort flag
		 */
		if(tfrabtf) {
			kb_puts("Write Error, File Truncated\r\n");
		}
		/*
		 * done
		 */
		ftpstate = 0;
		tfrabtf = 0;
		fcnt = 0;
		/*
		 * how long to transfer
		 */
		if(verbose) {
			/*
			 * Time calculation
			 */
			etime = elapsed(start);
			ei = etime/TICKSPERSEC;
			if(!ei) {
				ei = 1;
				ef = 0;
				etime = TICKSPERSEC;
			} else {
			/*	ei = ei;	*/
				ef = (10L*(etime%TICKSPERSEC))/TICKSPERSEC;
			/*	etime = etime;	*/
			}
			/*
			 * Rate calculation
			 */
			ri  = (tbytes*TICKSPERSEC)/etime;
			rf  = ri%1000;
			ri /= 1000;

			kbprintf(
	"Transferred %ld bytes in %d.%d seconds (%d.%03d kbytes/sec)\r\n",
				tbytes,ei,ef,ri,rf);
		}
		/*
		 * close connection
		 */
		skclose(ftpdskt);
		if(bell)
			kb_out(7);
	}
	/*
	 * Release socket when closed
	 */
	if(ftpdskt >= 0 && ftpstate == 0) {
#ifdef	DEBUGOPTION
if(debug&0x40) {
		printf("userftpd : ftpdskt = %d, state = %d\r\n",
			ftpdskt,skstate(ftpdskt));
}
#endif
		ftpdskt = skrelease(ftpdskt,0);
	}
}

/*
 * 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
 */

static int getword(string,word)
char *string;
register char *word;
{
	register char *p;
	register int i;
	char *q;

	i = 0;
#ifdef	DEBUGOPTION
if(debug&0x80) {
	printf("getword: string is %s\r\n",string);
}
#endif
	/*
	 * skip leading blanks
	 */
	p = stpblk(string);
	if(!(*p)) {
		/*
		 * no words in string
		 */
		word[0] = '\0';
		return(FALSE);
	}
	if(*p == '!') {
		/*
		 * ! is a word
		 */
		word[0] = *p;
		word[1] = '\0';
		strcpy(string,++p);
		return(TRUE);
	}
	if(*p == '\"') {
		/*
		 * word delimited by quotes
		 */
		while(p[++i] && p[i] != '\"')
			word[i-1] = p[i];
		word[i-1] = '\0';
		if(!p[i]) {
			kb_puts("Missing \". Assumed at end of string.\r\n");
		} else {
			i++;
		}
		q = p+i;
	} else {
		/*
		 * get word, max len 50
		 */
		q = stptok(p, word, 50, " \t\r\n");
	}
     	/*
	 * remove trailing blanks
	 */
	p = stpblk(q);
	/*
	 * remove extracted stuff
	 */
	strcpy(string,p);
	return(TRUE);
}

/*
 * finduniq:
 *
 * find name that is a unique prefix of one of the entries in
 * a list.  Return position of the entry,
 * NONE if none, AMBIGUOUS if more than one.
 */

static int finduniq(name,list,listsize)
char *name, *list[];
int listsize;
{
	register int i,j,ilen;

	j = NONE;
	ilen = strlen(name);
	for(i=0; i<listsize; i++) {
		/*
		 * prefix
		 */
		if(!strncmp(name,list[i],ilen)) {
			if(ilen == strlen(list[i]))
				/*
				 * exact match
				 */
				return(i+1);
			if(j != NONE) {
				/*
				 * more than one match
				 */
				j = AMBIGUOUS;
			} else {
				/*
				 * note prefix found
				 */
				j = i+1;
			}
	  	}
	}
	/*
	 * prefix
	 */
	return(j);
}

/*
 * checkevent
 *
 * get and process network events
 * returns ABORT on an abort, HAVEDATA if data available
 * on command connection, NONE if connection lost.
 * TRUE if no relevant event.
 */

static int checkevent()
{
	int cl,ev,dat;

	/*
	 * check for abort
	 */
	if(tstbrk())
		return(ABORT);
	/*
	 * do ftp stuff
	 */
	userftpd();
	/*
	 * look for events
	 */
	ev = Sgetevent(CONCLASS | MSGCLASS |
		       ERRCLASS | ABORTCLASS, &cl,&dat);
	if(ev) {
	    switch(cl) {
		case ABORTCLASS:	/* abort */
		    ftpexit(ABORT);
		    break;

		case ERRCLASS:		/* error messages */
		case MSGCLASS:		/* messages */
		    kb_puts(skerrstring(ev));
		    kb_nline();
		    break;

		case CONCLASS:		/* connection */
		    if(dat == ftpcskt) {
			switch(ev) {
			    case CONDATA:
				/*
				 * data has arrived
				 */
				return(HAVEDATA);

			    case CONCLOSE:
				/*
				 * connection lost
				 */
				skclose(ftpdskt);
				skclose(ftpcskt);
				linked = FALSE;
				return(NONE);

			     default:
				break;
			}
			break;
		    }
		    break;

		default:
		    break;
	    }
	}
	return(TRUE);
}

/*
 *  opncap
 *
 *  open the capture file
 */
FILE *
opncap(filspc,mode)
char *filspc,*mode;
{
	FILE *fp;

	if((fp = fopen(filspc, mode)) == NULL) {
	  kbprintf("Command Aborted: File %s failed to open.\r\n\n", filspc);
	}
	return(fp);
}

/*
 * Remote filespec
 */
char *rmtfile(st)
char *st;
{
	register char *t,*u;

	/*
	 * discard device spec and any subdirectories
	 */
	t = st;
	while(
		(u = strrchr(t,':')) != NULL ||
		(u = strrchr(t,'/')) != NULL ||
		(u = strrchr(t,'\\')) != NULL
	) { t = ++u; }

	rtparse(t);
	sprintf(rmtname,"%s.%s", rtname(),rtext());
	return(rmtname);
}

/*
 * getnname:
 *
 * get next name from captured list
 * names delimited by newlines - <CR> or <LF>
 */

static int getnname(word)
register char *word;
{
	register int chr;
	register char *str;

	if(capfp == NULL) {
		return(FALSE);
	}

	/*
	 * skip initial newlines
	 */
	while((chr = getc(capfp)) != EOF) {
		if (chr != '\n' && chr != '\r') {
			break;
		}
	}
	if(chr == EOF || chr == '\0') {
		return(FALSE);
	}

	str = word;
	*(word++) = chr;

	while((chr = getc(capfp)) != EOF) {
		if(chr == '\n' || chr == '\r' || chr == '\0') {
			break;
		} else {
			*(word++) = chr;
		}
	}
	*word = '\0';

#ifdef	DEBUGOPTION
if(debug&0x100) {
	printf("getnname: name is %s\r\n",str);
}
#endif

	if(chr == '\0') {
		ungetc(chr,capfp);
		return(TRUE);
	}
	if(chr == EOF) {
		return(TRUE);
	}


	/*
	 * skip trailing newline
	 */
	while((chr = getc(capfp)) != EOF) {
		if(chr != '\n' && chr != '\r') {
			ungetc(chr,capfp);
			break;
		}
	}

	return(TRUE);
}

static VOID strlwr(st)
register char *st;
{
	while(*st) {
		*st = tolower(*st);
		*st++;
	}
}

static 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);
}

/*
 *  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();
		}
	}
}

/*
 * File Scanner
 *
 * If not a wild card process, verifies existance of file
 * If a wild card process, returns first matching file
 * If file does not exist or no files match, returns NULL
 */
char *filnam(st,p,w)
char *st;
register char *p;
int w;
{
	if(w) {
		if(p == NULL) {
			/*
			 * get first name,
			 */
			if((p = firstname(st)) == NULL) {
				/*
				 * if no expansions
				 */
				kbprintf("No file(s) match %s\r\n",st);
			}
		} else {
			/*
			 * get next name
			 */
			p = nextname();
		}
	} else {
		/*
		 * no expansion
		 */
		p = rtfile(st,"");
		if((iop = fopen(p,"r")) == NULL) {
			kbprintf("No File %s\r\n", p);
			p = NULL;
		} else {
			fclose(iop);
		}
	}
	return(p);
}

/*
 * Check for wild card matches
 */
char *firstname(st)
char *st;
{
	if((iop = fwild(rtwfile(st,""), "r"))==NULL)
		return(NULL);
	return(nextname());
}

/*
 * Get next matching file name
 */
char *nextname()
{
	register char *p,*q;

	if(fnext(iop)!=NULL) {
		q = filename;
		p = iop->io_name;
		do {
			*q++ = tolower(*p);
		} while(*p++);
		return(filename);
	} else {
		iop = NULL;
		return(NULL);
	}
}

/*
 * Close open name file on aborts
 */
char *lastname()
{
	if(iop!=NULL) {
		fclose(iop);
		iop = NULL;
	}
	return(NULL);
}
