SRI-NOSC/ncpp/ftp-u/userftp.c.orig

#/*
Module Name:
	userftp.c

Installation:
	cc -O -f -s userftp.c -lj;:	compile the standard version
	su cp a.out /usr/bin/ftp;:	move it into the system library
	ed - userftp.c;:		make a version with a big buffer
/#define.*termsize/s,termsize.*,termsize 8192,
w bigftp.c
q
	cc -O -f -s bigftp.c -lj;:	compile the big version
	su cp a.out /usr/bin/bigftp;:	move it into the system library
	rm a.out bigftp.c;:		remove evidence

Function:

Globals contained:

Routines contained:

Modules referenced:

Modules referencing:

Compile time parameters and effects:

Module History:
*/
#
/* defines */

#define	termsize	1024
#define	CR		015
#define LF		012
#define ECHO		010

/* defines for the type field of parameterized open to network */

#define	o_direct	01	/* icp |~  direct */
#define o_server	02	/* user | server */
#define o_init		04	/* listen | init */
#define o_specific	010	/* general  | specific */
#define o_duplex	020	/* simplex | duplex */
#define o_relative	040	/* absolute | relative */

/* arrays and structs */

int  tty[3];			/* holds tty state bits */
char termbuf[ termsize ];	/* data buffer into which most data goes */
char inputbuffer[512];		/* main buffer for primary input */
char hostname[80];		/* permanently holds full host name for data connections */
char *comarr[]
{
	"get",
	"put",
	"abort",
	"ascii",
	"bin",
	"tenex",
	"bye",
	"end",
	"log",
	"!",
	   0
};

int (*funcarr[])()
{
	&net_write,
	&get,
	&put,
	&abort,
	&asciimode,
	&binmode,
	&tenexmode,
	&done,
	&done,
	&log,
	&unixcommand
};

struct openparams		/* struct for parameterized network opens */
{

	char o_op;		/* opcode for kernel & daemon - unused here */
	char o_type;		/* type for connection see defines below */
	int o_id;		/* id of file for kernel & daemon - unused here */
	int o_lskt;		/* local socket number either abs or rel */
	int o_fskt[2];		/* foreign skt either abs or rel */
	char o_frnhost;		/* foreign host number */
	char o_bsize;		/* bytesize for the connection */
	int o_nomall;		/* nominal allocation for the connection */
	int o_timeo;		/* number of secs before time out */
	int  o_relid;		/* fid of file to base a data connection on */

} openparams;

/* integer decs */

int termbytes;			/* number of bytes currently in termbuf */
int local_data_fid;		/* file descriptor of local file assoc w/ data conn */
int for_data_fid;		/* file descriptor of data connection */
int netfid;			/* file descriptor for telnet command connection */
int childpid;			/* process id of child data connection process */
int ascii	1;			/* flag =1 says crlf->lf or lf->crlf; 0 no map */
char *mapspace;			/* pointer to extra space for lf->crlf */
int weshould	1;		/* says when to stop */
int wantlf	0;		/* used by crlf_to_lf */
long bytestransferred;	/* for calculating statisics */
long starttime;		/* time at which an xfer started */
int gottime;
long stoptime;		/* time at which an xfer completed */

char *inputptr;			/* primary input - current pointer */
int inputcount;			/* primary input - count remaining */

extern int errno;		/* extern for perror */
/*name:
	user ftp 

function:
	to transfer data files to/from the local host to a foreign server

algorithm:
	open the telnet command connection to the host specified
	( if the hostname is not specified as a param, get it )
	forever, 
		read any awaiting data from terminal

		if any data was gotten see if it is a 
		known command( comarr ) if so do what 
		is needed, otherwise tack on a crlf and
		send it down the telnet command connection.

		see if there is any data to be read from the net
		if so read it.

		if any data was read in from the net, write it
		on the controling terminal making crlf into lf

parameters:
	host name specified on the command line

returns:
	when done or error

globals:
	termbuf=
	termbytes=
	argc
	argv=
	weshould

calls:
	getnullstr
	connftpserver
	term_read
	decodeanycommands
	net_read
	term_write

called by:
	user typing 'ftp <hostname>' on command line

history:
	initial coding 6/9/75 by S. F. Holmgren

*/

main( argc,argv )
char *argv[];
{

	/* if no hostname given ask for it */
	if( argc == 1 )
	{
		printf("Host: ");
		getnullstr(0);	/* get hostname null terminated */
		argv[1] = termbuf;	/* make pointer to hostname */
	}

	printf("Attempting Connection to %s\n",argv[1]);
	/* try and open the connection to the ftp server */
	connftpserver( argv[1] );
	printf("Connection Open\n");

	/* if we get here, the connection opened ok */
	if( fork() == 0 )
	{	/* this guy reads from net and writes to terminal */
		/* I don't want to die just because the user trys to
		   abort an inferior shell */
		signal(2,1);

		while( 1 )
		{
			/* get data from net */
			net_read();
			/* stick out on term */
			term_write();
		}
	}
	else
		/* this fella reads from term and writes to net */
		while( weshould )
		{
			/* get data from term */
			term_read( 0 );
			/* look for commands and pass data to net */
			decodeanycommands();
		}
}

/*name:
	connftpserver

function:
	make a connection to socket 3 of host specifed in hname

algorithm:
	build '/dev/net/<hostname>' in global hostname 
	setup for standard telnet connection to socket 3
	open the connection
		if error say so and exit


parameters:
	null terminated string containing the hostname to be
	connected to.

returns:
	'/dev/net/<hostname>' in hostname for use in opening data
	connections.

	netfid has the file descriptor of the opened connection

globals:
	hostname=
	netfid=
	openparams.o_fskt[1]=

calls:
	strmove
	open (sys)
	printf (sys)

called by:
	main

history:
	initial coding 6/9/75 by S. F. Holmgren

*/

connftpserver( hname )
char *hname;
{

	/* build host file name */
	strmove( hname, strmove( "/dev/net/",hostname ) );

	/* say connection is to socket 3 */
	openparams.o_fskt[1] = 3;

	/* make a connection to socket 3 */
	netfid = open( hostname,&openparams );
	if( netfid < 0 )
	{
		/* no soap */
		printf("Host is unavailable\n");
		exit();			/* exit program */
	}
}

/*name:
	term_read

function:
	Get a "\n" delimited line from the primary input source,
	put it in the buffer termbuf, and set the global variable
	termbytes to the character count in that line.

algorithm:
	Do reads into a main buffer, then take them one at a time,
	and transfer them to termbuf, doing new reads when necessary.
	The reason that the reads are not done into termbuf is that
	if the primary input is not a tty, a read may yield multiple
	lines, but termbuf should only get loaded with one line at
	a time.

parameters:
	starting index into termbuf to put data 

returns:
	bytes read in termbuf and number read in termbytes

globals:
	termbuf=
	termbytes=
	inputcount	Number of chars remaining in main buffer
	inputbuffer	The main buffer itself
	inputptr	Pointer to the next character in main buffer

calls:
	read (sys)

called by:
	main
	getnullstr

history:
	initial coding 6/9/75 by S. F. Holmgren
	modified for non-tty input 10/16/75 by M. Kampe
	modified to take index 9/27/76 by S. F. Holmgren

*/

term_read( indx )
{
	register int incnt;
	register char *outptr;
	register char *inptr;

	incnt = inputcount;
	inptr = inputptr;

	for(outptr = termbuf+indx; outptr < &termbuf[termsize-1]; )
	{	if (incnt <= 0)
		{	if ((incnt = read(0,inputbuffer,512)) <= 0)
			{	weshould = 0;
				break;
			}
			inptr = inputbuffer;
		}

		incnt--;
		if ((*outptr++ = *inptr++) == '\n') break;
	}

	inputcount = incnt;
	inputptr = inptr;
	termbytes = outptr - termbuf;
	return;
}

/*name:
	decodeanycommands

function:
	if there was data from terminal, see if it is a known
	command and if so call the correct procedure( funcarr )

algorithm:
	see function

parameters:
	termbytes (global)

returns:
	nothing

globals:
	termbytes
	funcarr

calls:
	net_write (thru funcarr)
	get	   (thru funcarr)
	put	   (thru funcarr)
	asciimode	(thru funcarr)
	binmode		(thru funcarr)
	tenexmode	(thru funcarr)
	whichcomm

called by:
	main

history:
	initial coding 6/9/75 by S. F. Holmgren

*/

decodeanycommands()
{

		/* call either data write or a command procedure */
		(*funcarr[ whichcomm() ]) ();
}

/*name:
	whichcomm

function:
	scan thru comarr and try and find a match between
	user input and any of the commands
	if one is found, return its relative index + 1

algorithm:
	while there are entries in comarr
		does this one compare to user input
			if so return relative entry

	been all thru and not found anything so send off

parameters:
	termbuf ( global )

returns:
	relative index into comarr in which string match was found
	otherwise zero

globals:
	comarr
	termbuf

calls:
	compare

called by:
	decodeanycommands

history:
	initial coding 6/9/75 by S. F. Holmgren

*/

whichcomm()
{

	register char **comp;
	register char **startp;

	comp = startp = &comarr;

	/* while there are strings in comarr */
	while( *comp )
		if( compare( *comp++,termbuf ) )	/* did we find one? */
			return( comp - startp );	/* return its index */
	return( 0 );					/* no takers */
}

/*name:
	net_write

function:
	tack a crlf on the end of the string
	send the data to the command telnet connection
	if there is an error then exit

algorithm:
	see function

parameters:
	termbuf (global)
	termbytes (global)

returns:
	if it does the write went ok otherwise the prog exits

globals:
	termbytes
	termbuf=

calls:
	write (sys)
	printf (sys)

called by:
	decodanycommands

history:
	initial coding 6/9/75 by S. F. Holmgren

*/

net_write()
{

	/* expects a null terminated string to be in termbuf */
	termbuf[ termbytes-1 ] = CR;	/* overwrite last char with cr */
	termbuf[ termbytes ] = LF;	/* add a line feed */

	if( write( netfid,termbuf,++termbytes ) < 0 )
	{
		printf("Host has closed the Connection\n");
		weshould = 0;
	}
}

/*name:
	get

function:
	open a data connection to the foreign host
	then spawn a process that reads from the net
	data connection and writes to a local file

algorithm:
	get local file name
	try and create the file
		if cant create say so and return
	get foreign file name
	formulate the retr command and send to network
	then open the data connection
	fork
		child
		while read from net 
			do any crlf to lf translation needed
			write to local file

parameters:
	local file name -- gotten from user
	foreign path name -- gotten from user

returns:
	nothing

globals:
	termbuf=
	termbytes=
	local_data_fid=
	for_data_fid=
	childpid=
	openparams.type=
	openparams.relid=
	openparmas.lskt=
	openparams.fskt[1]=
	starttime and stoptime	for statistics time stamps

calls:
	strmove
	net_write
	printf (sys)
	getnullstr
	creat (sys)
	write (sys)
	open (sys)
	close (sys)
	fork (sys)
	time (sys)
	crlf_to_lf

called by:
	decodeanycommands

history:
	initial coding 6/9/75 by S. F. Holmgren

*/

get()
{

	/* ask for local file name */
	printf(" Local File Name: ");
	getnullstr(0);			/* get file name null termed */

	/* try and open the file first, may be a special file */
	local_data_fid = open( termbuf,1 );
	/* if open failed and if create fails */
	if( local_data_fid < 0 && (local_data_fid = creat( termbuf,0666)) < 0 )
	{
		/* cant create file */
		printf(" Cant create %s\n",termbuf);
		return;
	}

	/* get foreign path name */
	printf(" Foreign File Name: ");

	/* move in first part of retr command */
	strmove("retr ",termbuf);
	getnullstr(5);			/* get file name null termed */
	net_write();			/* send data to network */

	/* set  up connection on default u+4 and s+5 */
	openparams.o_lskt = 4;		/* relative 4 off of local base */
	openparams.o_fskt[1] = 0;	/* any connection to 4 off local base */
	openparams.o_relid = netfid;	/* saw which local base to use */
	openparams.o_type = (o_relative|o_direct);
	openparams.o_nomall = 2048;	/* up allocation so stuff moves faster */

	/* fork and let child do the data handling */
	if( (childpid = fork()) == 0 )
	{
		bytestransferred = 0;
		setsignals();		/* so user knows when error occurs */
		/* try and open the data connection */
		if ( (for_data_fid = open( hostname,&openparams )) < 0 )
		{
			/* no soap */
			printf(" Cant Open Data Connection\n");
			return;
		}
		gottime = 0;
		sleep (2); /* give messages time to clear */
		while( (termbytes = read( for_data_fid,termbuf,termsize )) > 0 )
		{
			if (!gottime) {
				gottime++;
				time (&starttime);
				printf ("First byte of data received\n");
			}
			/* if i should map crlf to lf do it */
			if( ascii )
				crlf_to_lf(local_data_fid);
			else	write( local_data_fid,termbuf,termbytes );
			/* update number of bytes received */
			bytestransferred =+ termbytes;

		}
		time(&stoptime);		/* and get a closing timestamp */
		statistics();		/* say a little something for the folks */
		exit();			/* make child go away */
	}
	close( local_data_fid );
}

/*name:
	put

function:
	get data from local filing system and send to data connection
	based on telnet connection already open.

algorithm:
	get local pathname
	try and open file
		if cant say so and return
	get foreign pathname
	formulate and send 'stor' command
	open data connection
	spawn child which will read from local file
	and write to foreign file until eof

parameters:
	local file name -- gotten from user 
	foreign file name -- gotten from user
	telnet command connection already open

returns:
	nothing

globals:
	termbuf=
	termbytes=
	openparams.type=
	openparams.relid=
	openparams.lskt=
	openparams.frnskt[1]=
	starttime and stoptime	for a statistics time stamp

calls:
	strmove
	net_write
	getnullstr
	open (sys)
	printf (sys)
	read (sys)
	write (sys)
	exit (sys)
	close (sys)
	fork (sys)
	time (sys)

called by:
	decodeanycommands

history:
	initial coding 6/9/75 by S. F. Holmgren

*/

put()
{

	printf(" Local File Name: ");
	getnullstr(0);			/* get null termed fname */
	if( (local_data_fid = open( termbuf,0 )) < 0 )
	{						/* cant open local file */

		printf("Cant open %s\n",termbuf);
		return;
	}

	/* get foreign pathname */
	printf(" Foreign File Name: ");
	strmove( "stor ",termbuf );	/* move in first part of comm */
	getnullstr(5);			/* get null termed fname */
	net_write();			/* send it off to net conn */

	/* formulate the parameters for the data connection */
	openparams.o_type = (o_relative|o_direct);	/* set type */
	openparams.o_relid = netfid;			/* me the already open */
	openparams.o_lskt = 5;				/* u+5 from already open */
	openparams.o_fskt[1] = 0;			/* any conn 4 off local sock */

	/* start up a process to read from local and send to net */
	if( (childpid = fork()) == 0 )
	{
		bytestransferred = 0;	/* init number of bytes send */
		setsignals();		/* so user knows if error occured */
		/* try and open data connection */
		if( (for_data_fid = open( hostname,&openparams )) < 0 )
		{
			/* no soap */
			printf(" Cant Open Data Connection\n");
			return;
		}
		time(&starttime);	/* get a starting time stamp */
		gottime = 0;
		while( (termbytes=read(local_data_fid,termbuf,termsize)) > 0 )
		{
			gottime++;
			if ( ascii )		/* should i map */
				lf_to_crlf();	/* then do the map */
			else
				if( write( for_data_fid,termbuf,termbytes ) <= 0  )
				{
					printf(" Host Closed Data Connection\n");
					printf(" Aborting Transfer\n");
					break;
				}
			bytestransferred =+ termbytes;
		}
		time(&stoptime);		/* and get an ending time stamp */
		statistics();		/* tell the folks at home what you did */
		exit();					/* kill this process */
	}
	close( local_data_fid );
}

/*name:
	lf_to_crlf

function:
	to translate Line Feeds in termbuf into CRLF combinations

algorithm:
	copy the contents of termbuf into space twice as large
	everytime an lf is fund make it into a crlf

parameters:
	termbbuf
	termbytes

returns:
	nothing

globals:
	termbytes
	termbuf

calls:
	nothing

called by:
	put

history:
	initial coding 6/9/75 by S. F. Holmgren
*/

lf_to_crlf()
{

	register char *inp;
	register char *outp;
	register count;

	if( mapspace == 0 )
		mapspace = alloc ( termsize * 2 );

	inp = termbuf;
	outp = mapspace;
	count = termbytes;
	count++;

	while( --count )
	{
		if( *inp == LF )
		{	*outp++ = CR;
			termbytes++;
		}
		*outp++ = *inp++;
	}

	write( for_data_fid,mapspace,termbytes );
}
/*name:
	crlf_to_lf

function:
	to take the chars in termbuf, and translate any crlf character
	combinations into lf.

algorithm:
	thru the number of bytes in termbuf
		if there is a CR
			if there is an LF
				then copy the LF over the CR
				say there is one less byte
				in both termbytes and byte count

parameters:
	termbuf=
	termbytes=

returns:
	termbuf translated
	termbytes with the correct number of bytes

globals:
	termbuf=
	termbytes=

calls:
	nothing

called by:
	get
	term_write

history:
	initial coding 6/9/75 by S. F. Holmgren

*/

crlf_to_lf(outfile)
{

	register char *inp;
	register char *outp;
	register count;

	inp = outp = termbuf;
	count = termbytes;
	count++;		/* so can do auto dec in while */

	if( wantlf && *inp != LF )
		write( outfile,"\r",1 );
	wantlf = 0;
	/* thru the number of bytes */
	while( --count )
	{
		if( (*outp = *inp++) == CR )
		{
			/* is it crlf sequence */
			if( count-1 > 0 )	/* can we get another char */
				if( *inp == LF )	/* yes then wipe CF */
				{
					*outp = *inp++;
					count--;
					termbytes--;
				}
				else
					wantlf = 0;
			else
			{
				wantlf++;		/* say we need line feed */
				termbytes--;			/* say one less to write */
			}
		}
		outp++;					/* get next char */
	}
	write( outfile,termbuf,termbytes );
}

/*name:
	abort

function:
	to kill the child process and send 'abor' down the telnet
	command connection.

algorithm:
	kill child process
	make 'abort' into 'abor'
	write to the telnet connection

parameters:
	none

returns:
	nothing

globals:
	none

calls:
	kill (sys)
	net_write

called by:
	decodeanycommands ( thru funcarr )

history:
	initial coding 6/9/75 by S. F. Holmgren

*/

abort()
{

	/* signal child to die */
	kill( childpid,9 );

	/* make 'abort' into 'abor' */
	termbytes--;

	/* send 'abor' on down the telnet command connection */
	net_write();
}

/*name:
	asciimode

function:
	send an ascii mode command to the foreign host
	set the asciimode bit.
	gets done.

algorithm:
	really

parameters:
	none

returns:
	global ascii set to 1

globals:
	ascii =
	termbytes=

calls:
	printf (sys)
	strmove
	net_write

called by:
	decodeanycomands thru funcarr 

history:
	initial coding 6/9/75 by S. F. Holmgren

*/

asciimode()
{

	strmove("type a ",termbuf);
	termbytes = 7;
	net_write();
	ascii = 1;
}

/*name:
	binmode

function:
	send an image mode command to the foreign host
	reset the ascii flag
	gets done

algorithm:
	build a userftp 'type i' command
	send it to the network
	say we are in image mode
	set flag saying we are in image mode

parameters:
	none

returns:
	ascii set to 0

globals:
	ascii =
	termbuf=

calls:
	printf (sys)
	strmove
	net_write

called by:
	decodeanycommands thru funcarr

history:
	initial coding 6/9/75 by S. F. Holmgren


*/

binmode()
{
	strmove("type i ",termbuf);
	termbytes = 7;
	net_write();
	ascii = 0;
}

/*name:
	tenexmode

function:
	build and send a type l command to the foreign host
	reset the ascii mode flag

algorithm:
	build the type l command in termbuf
	set number of bytes in termbuf
	send it to the net with net_write
	reset the ascii mode flag

parameters:
	none

returns:
	foreign host in local data transfer mode

globals:
	termbuf=
	termbytes=

calls:
	strmove
	net_write

called by:
	decodeanycommands ( thru funcarr )

history:
	initial coding 10/6/75 by S. F. Holmgren

*/

tenexmode()
{
	strmove("type l ",termbuf);
	termbytes = 7;
	net_write();
	ascii = 0;
}
/*name:
	done		-		called in response to bye or end

function:
	to signal foreign host that we are leaving
	to signal main to exit

algorithm:
	build userftp 'bye' command
	send it off to network
	set weshould to zero

parameters:
	none

returns:
	weshould set to zero

globals:
	termbytes=
	weshould=

calls:
	strmove
	net_write

called by:
	decodeanycommands thru funcarr

history:
	initial coding 7/1/75 by S. F. Holmgren

*/

done()
{

	strmove("bye ",termbuf);	/* load in/out buff with ftp by comm */
	termbytes = 4;			/* say there is some data */
	net_write();			/* tell foreign host were going */
	weshould = 0;		/* tell main loop to bag it */
}

/*name:
	log		-	translates usercode password into ftp user
				pass commands

function:
	see name

algorithm:
	get usercode
	send to net
	set tty to no echo
	get password
	send to net
	set tty to echo
	get account
	if none return
	if any, send to net

parameters:
	none

returns:
	user, pass and possibly acct commands sent to net

globals:
	termbuf=
	termbytes=

calls:
	strmove
	net_write
	getnullstr

called by:
	main
	decodeanycommands thru funcarr

history:
	initial coding 7/1/75 by S. F. Holmgren

*/

log()
{
	register savetrmbytes;
	printf("Username:");		/* get user name */
	strmove("user ",termbuf);	/* formulate piece of user comm */
	getnullstr(5);			/* formulate rest of user comm */
	/* stick crlf on the end of string and set up for more from term */
	termbuf[ termbytes-1 ] = CR;
	termbuf[ termbytes ] = LF;
	termbytes++;

	gtty(0,tty);		/* get terminal options */
	tty[2] =& ~ECHO;		/* reset echo */
	stty( 0,tty );		/* do it to terminal */

	printf("Password:");		/* ask for password */
	strmove("pass ",&termbuf[ termbytes ]);	/* form piece of pass command */
	getnullstr(5+termbytes);			/* form rest of pass command */
	savetrmbytes = termbytes;
	termbuf[termbytes-1] = CR;
	termbuf[termbytes] = LF;
	termbytes++;

	gtty( 0,tty );		/* get term options */
	tty[2] =| ECHO;		/* start echoing again */
	stty( 0,tty );		/* do it */
	printf("\n");			/* echo cr after password */

	printf("Account:");		/* ask for his account */
	strmove("acct ",&termbuf[ termbytes ]);	/* form start of acct command */
	getnullstr(5+termbytes);			/* get the guy's account */
	if( termbytes == 7+savetrmbytes )	/* if the guy doesnt want one */
		termbytes = savetrmbytes;
	net_write();			/* send it if he does */
}

/*name
	term_write

function:
	if there is any data from the net take it and print
	it on the controlling teletype

algorithm:
	see function

parameters:
	termbytes
	termbuf (global)

returns:
	some data on the users terminal

globals:
	termbuf
	termbytes

calls:
	crlf_to_lf
	write (sys)

called by:
	main

history:
	initial coding 6/9/75 by S. F. Holmgren

*/

term_write()
{

		/* translate crlf -> lf */
		crlf_to_lf(1);
}

/*name:
	net_read

function:
	to check if there is data to read from the net
	if so read it into termbuf and set termbytes
	otherwise
	set termbytes to zero

algorithm:
	see function

parameters:
	netfile

returns:
	data into termbuf (global)

globals:
	termbytes=
	termbuf=

calls:
	gtty (sys)
	read (sys)

called by:
	main

history:
	initial coding 6/9/75 by S. F. Holmgren

*/

net_read()
{

		/* get data from net */
		if ( ( termbytes = read( netfid,termbuf,termsize )) <= 0 )
			/* if there were errors then exit */
			exit();
}

/*name:
	strmove

function:
	copy a null terminated string into a vector 

algorithm:
	while there is data copy the data

parameters:
	src	-	char pointer to a null termed string
	dest	-	char pointer to a place to stick the string

returns:
	pointer to the null at the end of the destination string

globals:
	none

calls:
	nothing

called by:
	connftpserver
	get
	put

history:
	initial coding 6/9/75 by S. F. Holmgren

*/

strmove( src,dest )
char *src;
char *dest;
{

	register char *srcp;	/* for speed */
	register char *destp;	/* for speed */

	srcp = src;
	destp = dest;

	/* copy str including null at the end */
	while( (*destp = *srcp++) ) destp++;

	/* return pointer to the null at the end */
	return( destp );
}

/*name:
	getnullstr

function:
	to read a string in from the terminal into termbuf 
	and stick a null on the end.
	the index says where to read the data in relative to the
	beginning of termbuf.  That index is also reflected in
	determining a new value for termbytes

algorithm:
	call term_read to get data from terminal into right place
	stick a null over the last character in the string,
	( replacing the LF that is appened to each stnd read from term )

parameters:
	index	-	location within termbuf to start putting chars

returns:
	a null terminated string of input from the terminal in termbuf
	with termbytes set to the right number of bytes

globals:
	termbuf=
	termbytes=

calls:
	term_read

called by:
	main
	get
	put

history:
	initial coding 6/9/75 by S. F. Holmgren
	modified to call term_read 9/27/76 by S. F. Holmgren

*/

getnullstr( index )
{

	/* call term_read to get chars into right place */
	term_read( index );
	/* make into null termed string */
	termbuf[ termbytes-1 ] = 0;
}

/*name:
	compare

function:
	compare two strings for the length of the first for sameness

algorithm:
	while there is data to compare
		if they dont compare return failure
	return success

parameters:
	null terminated comparator string
	comparator candidate

returns:
	0 if didnt compare
	1 if compared

globals:
	none

calls:
	nothing

called by:
	whichcomm

history:
	initial coding 6/9/75 by S. F. Holmgren

*/

compare( str1,str2 )
char *str1;
char *str2;
{

	register char *str1p;	/* for speed */
	register char *str2p;	/* for speed */

	str1p = str1;
	str2p = str2;

	/* while there is data in str1 */
	while( *str1p )
		if( *str1p++ != *str2p++ )	/* if they dont compare */
			return( 0 );
	/* got to here must have all compared - return success */
	return( 1 );
}

setsignals()
{
	extern int (*sayerr)();
	extern int (*statistics)();

	signal(4,&sayerr);
	signal(5,&sayerr);
	signal(6,&sayerr);
	signal(7,&sayerr);
	signal(8,&sayerr);
	signal(10,&sayerr);
	signal(11,&sayerr);
	signal(12,&sayerr);
	signal(13,&sayerr);
}

sayerr()
{
	perror("**** FTP Internal Error ****");
}

statistics()
{
	long net_time;

	/* say how many bytes transferred and ultimately what baud rate */
	if (!gottime) {
		printf ("Sorry, no data transferred\n");
		return;
	}
	printf("%L bytes transfered", bytestransferred);
	net_time = stoptime - starttime;
	printf(" over a period of %L seconds",net_time);
	if (!net_time) net_time = 1; /* no divide's by zero, plz */
	printf(" (%L baud)\n",
		(bytestransferred<<3)/net_time );
}
/*name:
	unixcommand

function:
	to fork an inferior shell and execute a command which the
	user gives while in ftp.  Such commands are recognized by
	the fact that the first character of the line is an '!'
	(exclamation point).

algorithm:
	Null terminate the string in termbuf, do a fork, and have
	the new guy exec a shell (passing it a "-c" and a pointer
	to the shell command in termbuf).  The old guy will wait
	until the new guy exits.

parameters:
	None, its only "argument" is found in termbuf.

returns:
	a perfunctory 0

globals:
	termbuf	- where the command to be executed is
	termbytes - to know how many characters are in line

calls:
	fork	- to fork the shell
	execl	- to pass its arguments
	wait	- to await the death of the inferior shell
	signal	- to ignore interrupts (and restore them later)

called by:
	decodeanycommands ( thru funcarr )

history:
	initial coding 9/30/75 by M. Kampe

*/

unixcommand()
{
	register int i;
	register int j;
	int waitstatus;

	termbuf[termbytes-1] = '\000';
	i = fork();
	if (i<0) return(0);
	if (i)
	{	j = signal(2,1);
		while( wait(&waitstatus) != i);
		signal(2,j);
		printf("!\n");
		return(0);
	}
	else
	{	signal(2,0);
		execl("/bin/sh","sh","-c",&termbuf[1], 0);
		exit(0);
	}
}