/* filename	: main.c
 * purpose	: dispatching and some functions for cohulip.
 *
 * notes	: derived from ka9q/k5jb/NetBSD 09 and
 *		: also uses new code. There is lots of
 *		: code in this package of unknown origin.
 *		: but is is safe to say that at least this
 *		: file is still under Phil Karn's 1988
 *		: KA9Q copyright. Other files contain
 *		: copyright notices from other people or
 *		: organizations.
 *
 *		: it is structured in such a way that new
 *		: code or devices can be added from exiting
 *		: ka9q or k5jb-k29 code. To do so, commands
 *		: need to be entered into the command tables
 *		: and if a device support is added, check out
 *		: the makepiston() function in nedl.c. Such a
 * 		: call adds the device to the engine defined
 *		: in motor.c. This version is specifcally built
 *		: for COHERENT 4.2 and supports the ne CON style
 *		: ethernet driver. Randy Wright, Jan 1994.
 *
 *			-  rw@rwsys.wimsey.bc.ca
 *
 *
 */

#define HOSTNAMELEN 64
unsigned restricted_dev=1000;

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <signal.h>

#include "options.h"
#include "config.h"
#include "sokname.h"
#include "global.h"
#include "mbuf.h"
#include "netuser.h"
#include "timer.h"
#include "icmp.h"
#include "iface.h"
#include "ip.h"
#include "tcp.h"
#include "ftp.h"
#include "telnet.h"
#include "remote.h"
#include "motor.h"
#ifdef _FINGER
#include "finger.h"
#endif

#include "session.h"
#include "cmdparse.h"

#ifdef  ASY
#include "asy.h"
#include "slip.h"
#endif

#include "unix.h"
#include "unixopt.h"

#ifdef  TRACE
#include "trace.h"
/* Dummy structure for loopback tracing */
struct interface loopback = { NULLIF, "loopback" };
#endif

/* various file and path names used by fileinit() */

extern char *homedir,*startup,*userfile,*hosts,*spool,*mailspool,
		*mailqdir,*mailqueue,*routeqdir,*alias,*helpbox,*public;
#ifdef _FINGER
extern char *fingersuf,*fingerpath;
#endif
char defaultnethome[] = "/usr/net";
extern char *netexe;

extern struct interface *ifaces;
void version();
extern struct mbuf *loopq;
extern FILE *trfp;
extern char trname[];

int Keyhit = -1;
int mode;
int exitval;	/* used by doexit() */

static char *logname;
static FILE *logfp;

char badhost[] = "Unknown host %s\n";
char hostname[HOSTNAMELEN];
unsigned nsessions = NSESSIONS;
int32 resolve();
char *strncpy();
int16 lport = 1001;
char prompt[] = "net> ";
char nospace[] = "No space!!\n";        /* Generic malloc fail message */



int background = 0;
int backgrd = 0;	/* used by shell layer management - K5JB */
extern int reportquit_flag;
void report_quit();
int io_active = 0;

static char escape = 0x1d;      /* default escape character is ^] */


/* Enter command mode */
int
cmdmode()
{
	void cooked();
#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif

	if(mode != CMD_MODE){
		mode = CMD_MODE;
		cooked();
		printf(prompt);
		fflush(stdout);
	}
	return 0;
}

int	/* removed static to make this the only sane exit from program */
doexit()	/* left declaration as int to match cmd list */
{
	void iostop(),exit();
#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif

#ifdef TRACE
	if (trfp != stdout)
	  fclose(trfp);
#endif

	if (!background)
		iostop();	/* this function will exit() */

	exit(exitval);
	return(-1);
}

static
int
dohostname(argc,argv)
int argc;
char *argv[];
{
#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif

	if(argc < 2)
		printf("%s\n",hostname);
	else
		strncpy(hostname,argv[1],HOSTNAMELEN);
	return 0;
}

static
int
dolog(argc,argv)
int argc;
char *argv[];
{
	char *make_path();
	void free();
#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif

	if(argc < 2){
		if(logname != NULLCHAR)
			printf("Log: %s. log off stops.\n",logname);
		else
			printf("Log off. log <fn> starts.\n");
		return 0;
	}
	if(logname != NULLCHAR){
		free(logname);
		logname = NULLCHAR;
	}
	if(strcmp(argv[1],"off") == 0)
		return 0;
	if(argv[1][0] == '/' || argv[1][0] == '\\')
		logname = make_path("",argv[1],0);
	else
		logname = make_path(homedir,argv[1],1);
	return 0;
}

static
int
dohelp()
{
	extern struct cmds cmds[];
	register struct cmds *cmdp;
	int i;
#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif

	printf("Main commands:");	/* first command is "" and will do \n */
	for(i=0,cmdp = cmds;cmdp->name != NULLCHAR;cmdp++,i++){
		printf("%-15s",cmdp->name);
		if(!(i % 5))
			printf("\n");
	}
	printf("\n");
	return 0;
}

static
int
dohelp1(argc,argv)
int argc;
char *argv[];
{
	char *make_path();
	void free();
	char *pp;
	int i;
	char helpname[16];	/* should be big enough even for Unix */
	char *defaulthelp = "net";
	int dohelpfile();
#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif

	if(argc < 2)
		pp = defaulthelp;
	else
		pp = argv[1];

	for(i=0;i<11;i++){
		helpname[i] = pp[i];
		if(helpname[i] == '\0')
			break;
	}
	helpname[i] = '\0';	/* to be sure */
	strcat(helpname,".hlp");
	pp = make_path(homedir,helpname,1);
	if(pp != NULLCHAR){
		dohelpfile(pp,0);    /* ignore return value */
		free(pp);
	}
	return 0;
}

static  	/* primitive way to peek at our mail, page at a time */
int
domail(argc,argv)
int argc;
char *argv[];
{
	char *make_path();
	void free();
	int atoi(), dohelpfile();
	char *pp;
	char *mfname;
	int i;
	char hostmail[16];	/* should be big enough even for Unix */
#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif

	if(argc < 2)
		mfname = hostname;
	else
		mfname = argv[1];
	for(i=0;i<11;i++){
		if(mfname[i] == '.' || mfname[i] == '\0')
			break;
		hostmail[i] = mfname[i];
	}
	hostmail[i] = '\0';
	strcat(hostmail,".txt");
	pp = make_path(mailspool,hostmail,1);
	if(argc == 3)
		i = 22 * atoi(argv[2]);
		else
			i = 0;
	if(pp != NULLCHAR){
		if(dohelpfile(pp,i) == -1)
			printf("Or there is no mail\n");
		free(pp);
	}
	return 0;
}

int
dohelpfile(file,jump)
char *file;
int jump;
{
#define HFLINE 120	/* arbitrary, but this will keep
								cycles down on long lines */
	extern int Keyhit, kbread();
	FILE *hfile;
	extern char escape;
	extern char nospace[];
	char *hbuf;
	int i;
	void keep_things_going(),check_kbd(),free();
#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif

	Keyhit = -1;
	if((hbuf = (char *)malloc(sizeof(char) * HFLINE)) == NULLCHAR){
		perror(nospace);
		return(0);
	}
	if((hfile = fopen(file,"r")) == NULLFILE){
		printf("Can't find %s\n",file);
		fflush(stdout);
		free(hbuf);
		return(-1);
	}
	if(jump)
		for(i=0;i<jump;i++)
			if(fgets(hbuf,HFLINE,hfile) == NULLCHAR){
				fclose(hfile);
				free(hbuf);
				return(0);
			}

	for(;;){
		for(i=0;i<22;i++){
			if(fgets(hbuf,HFLINE,hfile) == NULLCHAR || hbuf[0] == 0x1a){
				Keyhit = 0; /* fake an escape */
				break;
			}
			printf(hbuf);	/* already has EOL appended */
			fflush(stdout);
			keep_things_going();
		}
		if(Keyhit == 0)
			break;
		printf("\nHit any key to continue, ");

		printf("escape key to quit...");



		fflush(stdout);
		do{
			keep_things_going();

			Keyhit = kbread();


		}while(Keyhit == -1);

		if(Keyhit == escape)
			break;

		Keyhit = -1;
		printf("\n\n");
		fflush(stdout);
	}	/* for(ever) while there is a file and no escape */
	printf("\n");
	fflush(stdout);
	fclose(hfile);
	free(hbuf);
	return(0);
}

#ifdef _TELNET
int
doecho(argc,argv)
int argc;
char *argv[];
{
	extern int refuse_echo;
#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif

	if(argc < 2){
		if(refuse_echo)
			printf("Refuse\n");
		else
			printf("Accept\n");
	} else {
		if(argv[1][0] == 'r')
			refuse_echo = 1;
		else if(argv[1][0] == 'a')
			refuse_echo = 0;
		else
			return -1;
	}
	return 0;
}
#endif /* _TELNET */

#ifdef _TELNET
/* set for unix end of line for remote echo mode telnet */
int doeol(argc,argv)
int argc;
char *argv[];
{
	extern int unix_line_mode;
#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif

	if(argc < 2){
		if(unix_line_mode)
			printf("Unix\n");
		else
			printf("Standard\n");
	} else {
		if(strcmp(argv[1],"unix") == 0)
			unix_line_mode = 1;
		else if(strcmp(argv[1],"standard") == 0)
			unix_line_mode = 0;
		else {
			return -1;
		}
	}
	return 0;
}
#endif /* _TELNET */

int subcmd();

/* Attach an interface
 * Syntax: attach <hw type> <I/O address> <vector> <mode> <label> <bufsize>
		<speed>
 */
int doattach(argc,argv)
int argc;
char *argv[];
{
	extern struct cmds attab[];
#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif

	return subcmd(attab,argc,argv);
}



/* Manipulate I/O device parameters */
int doparam(argc,argv)
int argc;
char *argv[];
{
	register struct interface *ifp;
#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif

	for(ifp=ifaces;ifp != NULLIF;ifp = ifp->next){
		if(strcmp(argv[1],ifp->name) == 0)
			break;
	}
	if(ifp == NULLIF){
		printf("Interface \"%s\" unknown\n",argv[1]);
		return 1;
	}
	if(ifp->ioctl == NULLFP){
					 printf("Not avail.\n");
		return 1;
	}

	/* Pass rest of args to device-specific code */
	return (*ifp->ioctl)(ifp,argc-2,argv+2);
}


/* Command lookup and branch table */ /* added bootp, gettime, domain RLW */
int go(), dodomain(), dotelnet(),doclose(),
	doreset(),dotcp(), dotrace(),doescape(),doroute(),doip(),doarp(),
	dosession(),doftp(), dostart(),dostop(),dosmtp(),doudp(),dodump(),
	dorecord(),doupload(), dokick(),domode(),doshell(),dodir(),
#ifdef FORWARD
	doforward(),
#endif

	docd(),doatstat(),doping(),doremote();

int memstat();


#ifdef ETHER
int doetherstat();
#endif


#ifdef _FINGER
int dofinger();
#endif

#ifdef SOKNAME
int issok = 1;
int 
flipsok()	/* int to be consistent with rest of commands */
{
#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif

	issok = !issok;
	printf("Socket names are %s\n",issok ? "on" : "off");
	return(0);
}
#endif

struct cmds cmds[] = {
	/* The "go" command must be first */
{	"",             go,             0, NULLCHAR,    NULLCHAR },
{	"!",            doshell,        0, NULLCHAR,    NULLCHAR },

#if     defined(ETHER)
{	"arp",          doarp,          0, NULLCHAR,    NULLCHAR },
#endif

{	"attach",       doattach,       2,		
"attach <hardware> <hw specific options>", NULLCHAR },
{	"cd",           docd,           0, NULLCHAR,    NULLCHAR },
{	"close",        doclose,        0, NULLCHAR,    NULLCHAR },
{	"dir",          dodir,          0, NULLCHAR,    NULLCHAR },
{	"domain",	dodomain,	0, NULLCHAR, 	NULLCHAR },
#ifdef _TELNET
{	"echo",         doecho,         0, NULLCHAR,
	"echo [refuse|accept]" },

{	"eol",          doeol,          0, NULLCHAR,		
	"eol options: unix, standard" },

#endif
{	"escape",       doescape,       0, NULLCHAR,    NULLCHAR },

#if defined(ETHER) || defined(NE)
{	"etherstat",    doetherstat,    0, NULLCHAR,    NULLCHAR },
#endif  /* ETHER || NE */
{	"exit",         doexit,         0, NULLCHAR,    NULLCHAR },
#ifdef _FINGER
{	"finger",       dofinger,       0, NULLCHAR, NULLCHAR },
#endif
#ifdef FORWARD
{	"forward",      doforward,      0, NULLCHAR,    NULLCHAR },
#endif
{	"ftp",          doftp,          2, "ftp <address>",     NULLCHAR },
{	"help",         dohelp1,        0, NULLCHAR,    NULLCHAR },
{	"hostname",     dohostname,     0, NULLCHAR,    NULLCHAR },
{	"ip",           doip,           0, NULLCHAR,    NULLCHAR },
{	"kick",         dokick,         0, NULLCHAR,    NULLCHAR },
{	"log",          dolog,          0, NULLCHAR,    NULLCHAR },
{	"memstat",      memstat,        0, NULLCHAR,    NULLCHAR },
{	"mail",         domail,         0, NULLCHAR,    NULLCHAR },
{	"param",        doparam,        2, "param <interface>", NULLCHAR },
{	"ping",         doping,         0, NULLCHAR,    NULLCHAR },
{	"pwd",          docd,           0, NULLCHAR,    NULLCHAR },
{	"record",       dorecord,       0, NULLCHAR,    NULLCHAR },
{	"remote",       doremote,       4, 
		"remote <address> <port> <command>",	NULLCHAR },
{	"reset",        doreset,        0, NULLCHAR,    NULLCHAR },
{	"route",        doroute,        0, NULLCHAR,    NULLCHAR },
{	"session",      dosession,      0, NULLCHAR,    NULLCHAR },
{	"shell",        doshell,        0, NULLCHAR,    NULLCHAR },
{	"smtp",         dosmtp,         0, NULLCHAR,    NULLCHAR },
#ifdef SOKNAME
{	"sokname",      flipsok,        0, NULLCHAR,    NULLCHAR },
#endif

#ifdef  SERVERS
{	"start",        dostart,        2, "start <servername>",NULLCHAR },
{	"stop",         dostop,         2, "stop <servername>", NULLCHAR },
#endif
{	"tcp",          dotcp,          0, NULLCHAR,    NULLCHAR },

#ifdef _TELNET
{	"telnet",       dotelnet,       2, "telnet <address>",  NULLCHAR },
#endif
#ifdef  TRACE
{	"trace",        dotrace,        0, NULLCHAR,    NULLCHAR },
#endif
{	"udp",          doudp,          0, NULLCHAR,    NULLCHAR },
#if defined(_TELNET)
{	"upload",       doupload,       0, NULLCHAR,    NULLCHAR },
#endif
{	"?",            dohelp,         0, NULLCHAR,    NULLCHAR  },
{	NULLCHAR,       NULLFP,         0, 
	"Unknown command; type \"?\" for list",   NULLCHAR }
};

#ifdef  SERVERS
/* "start" and "stop" subcommands */
int dis1(),echo1(),ftp1(),smtp1(),tn1(),rem1();

#if defined(UNIX) && defined(TELUNIX)
int tnix1();
#endif

#ifdef _FINGER
int finger1();
#endif

static struct cmds startcmds[] = {
{	"discard",      dis1,           0, NULLCHAR, NULLCHAR },
{	"echo",         echo1,          0, NULLCHAR, NULLCHAR },
#ifdef _FINGER
{	"finger",       finger1,        0, NULLCHAR, NULLCHAR },
#endif
{	"ftp",          ftp1,           0, NULLCHAR, NULLCHAR },
{	"smtp",         smtp1,          0, NULLCHAR, NULLCHAR },
#ifdef _TELNET
{	"telnet",       tn1,            0, NULLCHAR, NULLCHAR },
#endif

#if defined(UNIX) && defined(TELUNIX)
{	"telunix",      tnix1,          0, NULLCHAR, 
	"Could not start telunix - no pty?" },
#endif
{	"remote",       rem1,           0, NULLCHAR, NULLCHAR },
{	NULLCHAR,       NULLFP,         0,

#ifdef UNIX
#ifdef TELUNIX
#ifdef _FINGER
		"start options: discard, echo, finger, ftp, remote, smtp, telnet, telunix", NULLCHAR }
#else
		"start options: discard, echo, ftp, remote, smtp, telnet, telunix", NULLCHAR }
#endif
#else /* TELUNIX */
#ifdef _FINGER
		"start options: discard, echo, finger, ftp, remote, smtp, telnet", NULLCHAR }
#else
		"start options: discard, echo, ftp, remote, smtp, telnet", NULLCHAR }
#endif
#endif /* TELUNIX */
#else /* UNIX */
#ifdef _FINGER
		"start options: discard, echo, finger, ftp, remote, smtp, telnet", NULLCHAR }
#else
		"start options: discard, echo, ftp, remote, smtp, telnet", NULLCHAR }
#endif
#endif /* UNIX */
};

int ftp_stop(),smtp_stop(),echo_stop(),dis_stop(),tn_stop();
int dis0(),echo0(),ftp0(),smtp0(),tn0(),rem0();

#if defined(UNIX) && defined(TELUNIX)
int tnix0();
#endif

#ifdef _FINGER
int finger0();
#endif

static struct cmds stopcmds[] = {
{	"discard",      dis0,           0, NULLCHAR, NULLCHAR },
{	"echo",         echo0,          0, NULLCHAR, NULLCHAR },
#ifdef _FINGER
{	"finger",       finger0,        0, NULLCHAR, NULLCHAR },
#endif
{	"ftp",          ftp0,           0, NULLCHAR, NULLCHAR },
{	"smtp",         smtp0,          0, NULLCHAR, NULLCHAR },
#ifdef _TELNET
{	"telnet",       tn0,            0, NULLCHAR, NULLCHAR },
#endif
#if defined(UNIX) && defined(TELUNIX)
{	"telunix",      tnix0,          0, NULLCHAR,
	"Stop telunix failed, no server running" },
#endif
{	"remote",       rem0,           0, NULLCHAR, NULLCHAR },
{	NULLCHAR,       NULLFP,         0,
#ifdef UNIX
#ifdef TELUNIX
#ifdef _FINGER
		"stop options: discard, echo, finger, ftp, remote, smtp, telnet, telunix", NULLCHAR }
#else
		"stop options: discard, echo, ftp, remote, smtp, telnet, telunix", NULLCHAR }
#endif /* _FINGER */
#else /* TELUNIX */
#ifdef _FINGER
		"stop options: discard, echo, finger, ftp, remote, smtp, telnet", NULLCHAR }
#else
		"stop options: discard, echo, ftp, remote, smtp, telnet", NULLCHAR }
#endif /* _FINGER */
#endif /* TELUNIX */
#else /* UNIX */
#ifdef _FINGER
		"stop options: discard, echo, finger, ftp, remote, smtp, telnet", NULLCHAR }
#else
		"stop options: discard, echo, ftp, remote, smtp, telnet", NULLCHAR }
#endif /* _FINGER */
#endif /* UNIX */
};
#endif /* SERVERS */

char
*make_path(path_ptr,file_ptr,slash)
char *path_ptr;
char *file_ptr;
int slash;
{
	char *cp, *malloc();
#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif

	if ((cp = malloc(strlen(path_ptr) + strlen(file_ptr) + 2)) == NULLCHAR)
		perror(nospace);
	else {
		sprintf(cp, "%s%s%s", path_ptr,slash ? "/" : "",file_ptr);
		return(cp);
	}
	return(NULLCHAR);
}

/* found idea for this in sys5unix.c, but it wasn't completed - K5JB */
void
fileinit(cmdarg)
char *cmdarg;
{
	char *ep;
	extern char *getenv();
#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif

	if((ep = getenv("NETHOME")) == NULLCHAR)
		ep = defaultnethome;

	/*	if((ep = getenv("HOME")) == NULLCHAR)
			ep = homedir; */
	if(ep != homedir)
		homedir = make_path(ep,"",0);

	/* Replace each of the file name strings with the complete path */
	if(cmdarg == NULLCHAR)
		startup = make_path(ep,startup,1);
	else
		startup = make_path(ep,cmdarg,1);

	userfile = make_path(ep,userfile,1);
	hosts = make_path(ep,hosts,1);
	alias = make_path(ep,alias,1);
	helpbox = make_path(ep,helpbox,1);
#ifdef _FINGER
	fingerpath = make_path(ep,fingerpath,1);
#endif

	netexe = make_path(ep,"net",1);

	if((ep = getenv("PUBLIC")) != NULLCHAR)
		public = make_path(ep,"",0);
	if((ep = getenv("NETSPOOL")) != NULLCHAR)
		spool = make_path(ep,"",0);

	mailspool = make_path(spool,mailspool,1);
	mailqdir = make_path(spool,mailqdir,1);
	mailqueue = make_path(spool,mailqueue,1);
	routeqdir = make_path(spool,routeqdir,1);

}

/* Standard commands called from main */

/* If SOKNAME is defined, log messages of the form:
 * Mon Aug 26 14:32:17 1991 k5jb.okla.ampr:1003 open FTP
 * else:
 * Mon Aug 26 14:32:17 1991 44.78.0.2:1003 open FTP
 */

/*VARARGS2*/
void
log(tcb,fmt,arg1,arg2,arg3,arg4)
struct tcb *tcb;
char *fmt;
int arg1,arg2,arg3,arg4;
{

	long t,time();
	void rip();
	char *cp, *psocket();

#ifdef SOKNAME
	char *puname();
#endif

#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif

	if(logname == NULLCHAR)
		return;
	/* My Unix machine doesn't mind the "t" but some might */
	if((logfp = fopen(logname,"a+")) == NULLFILE)
		return;

	time(&t);
	cp = ctime(&t);
	cp[19] = '\0';	/* knock off the year, omit day below */

#ifdef SOKNAME
	fprintf(logfp,"%s %s - ",cp+4,puname(&tcb->conn.remote));
#else
	fprintf(logfp,"%s %s - ",cp+4,psocket(&tcb->conn.remote));
#endif
	fprintf(logfp,fmt,arg1,arg2,arg3,arg4);
	fprintf(logfp,"\n");
	fflush(logfp);
	fclose(logfp);
}

void
genlog(who,stuff)
char *who;
char *stuff;
{
extern FILE *logfp;
extern char *logname;
char *cp;
long t;
#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif

	if(logname == NULLCHAR)
		return;

	if((logfp = fopen(logname,"a+")) == NULLFILE)
		return;

	time(&t);
	cp = ctime(&t);
	cp[19] = '\0';	/* shorten a bit */
	fprintf(logfp,"%s %s - %s\n",cp+4,who,stuff);
	fclose(logfp);
}

/* Configuration-dependent code */

/* List of supported hardware devices */
int asy_attach(),pc_attach(),at_attach();


#ifdef  NE
int ne_attach();
#endif

struct cmds attab[] = {

#ifdef  ASY
	/* Ordinary PC asynchronous adaptor */
{	"asy", asy_attach, 8,
	  "attach asy 0 <ttyname> slip <label> 0 <mtu> <speed>",
		"Could not attach asy" },
#endif /* ASY */

#ifdef  NE
	/* NE1000/2000 Coherent Ethernet interface */
{ 	"nx", ne_attach, 7,
	"attach nx <address> <vector> arpa <label> <buffers> <mtu>",
	"Could not attach nx" },
#endif

{	NULLCHAR, NULLFP, 0,  "Unknown device",  NULLCHAR }
};

/* Protocol tracing function pointers */
#ifdef  TRACE
extern int ether_dump(), ip_dump();


int (*tracef[])() = {
	NULLFP, /* ax25 trace */
#ifdef  ETHER
	ether_dump,
#else
	NULLFP,
#endif
	ip_dump,
	NULLFP, /* APPLETALK */
	NULLFP /* SLFP */
};
#else /* TRACE */

int (*tracef[])() = { NULLFP }; /* No tracing at all */

/* the real dump() is defined in trace.c */

void
dump(interface,direction,type,bp)
struct interface *interface;
int direction;
unsigned type;
struct mbuf *bp;
{
}

#endif /* TRACE */

#ifdef  ASY

/* Attach a serial interface to the system
 * argv[0]: hardware type, must be "asy"
 * argv[1]: I/O address, e.g., "0x3f8"
 * argv[2]: vector, e.g., "4"
 * argv[3]: mode, may be:
 *          "slip" (point-to-point SLIP)
 *          "ax25" (AX.25 frame format in SLIP for raw TNC)
 *          "nrs" (NET/ROM format serial protocol)
 *          "slfp" (point-to-point SLFP, as used by the Merit Network and MIT
 * argv[4]: interface label, e.g., "sl0"
 * argv[5]: receiver ring buffer size in bytes
 * argv[6]: maximum transmission unit, bytes
 * argv[7]: interface speed, e.g, "9600"
 * argv[8]: optional ax.25 callsign (NRS only)
 *          optional command string for modem (SLFP only)
 */
int
asy_attach(argc,argv)
int argc;
char *argv[];
{
	register struct interface *if_asy;
	extern struct interface *ifaces;
	int16 dev;
	int mode,asy_init(),asy_send(),asy_ioctl(),asy_stop();
	extern int slip_send(),slip_raw();
	int atoi();
	void doslip(),slip_recv(),asy_speed(),free(), slipout();
	int asy_ioctl();
	extern int IORser[];

	argc = argc;
#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif

	if(nasy >= ASY_MAX){
		printf("Too many asynch controllers\n");
		return -1;
	}
	if(strcmp(argv[3],"slip") == 0)
		mode = SLIP_MODE;
	else {
		printf("Mode %s unknown for interface %s\n",
			argv[3],argv[4]);
		return(-1);
	}

	dev = nasy++;
	printf( "attaching %s as %s\n" , argv[2], argv[4] );
	/* Create interface structure and fill in details */
	if_asy = (struct interface *)calloc(1,sizeof(struct interface));
	if_asy->name = malloc((unsigned)strlen(argv[4])+1);
	strcpy(if_asy->name,argv[4]);
	if_asy->mtu = atoi(argv[6]);
	if_asy->dev = dev;
	if_asy->recv = doslip;
	if_asy->stop = asy_stop;


	switch(mode){
#ifdef  SLIP
	case SLIP_MODE:
		if_asy->ioctl = asy_ioctl;
		if_asy->send = slip_send;
		if_asy->output = NULLFP;        /* ARP isn't used */
		if_asy->raw = slip_raw;
		if_asy->flags = 0;
		slip[dev].recv = slip_recv;
		break;
#endif
	}
	if_asy->next = ifaces;
	ifaces = if_asy;
	asy_init(dev,argv[1],argv[2],(unsigned)atoi(argv[5]));
	asy_speed(dev,atoi(argv[7]));

		/* put it on the list of polls */
/* arg0 = fd, arg1 = r_call, arg2 = t_call, arg3 = p_call, arg4 = ifp
   arg5 = events RLW
  */
	if((makepiston(IORser[dev],doslip,slipout, NULLFP ,if_asy, POLLIN ))== -1) {
		printf( "error: cannot put %s on poll list\n",argv[4] );
		return(-1);
	}
/*** insert modem dialer here, runs off of attach argv[9], with
a send expect deal. ****/
/* optional modem dialer call */

#if defined(SLIP) && defined(MODEM_CALL)

	if((mode == SLIP_MODE) && (argc > 8)) {
		extern int modem_init();
		printf( "Initializing Modem\n" );
	    restricted_dev=dev;
	    if((modem_init(dev,argc-8,argv+8)) == -1) {
		printf("\nModem command sequence failed.\n");
		asy_stop(if_asy);
		ifaces = if_asy->next;
		free(if_asy->name);
		free((char *)if_asy);
		nasy--;
		restricted_dev=1000;
		return -1;
	    }
	    restricted_dev=1000;
	    return 0;
	}
#endif

/********************************/
	return 0;
}
#endif /* ASY */


/* Display or set IP interface control flags */
int domode(argc,argv)
int argc;
char *argv[];
{
	register struct interface *ifp;
#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif

	for(ifp=ifaces;ifp != NULLIF;ifp = ifp->next){
		if(strcmp(argv[1],ifp->name) == 0)
			break;
	}
	if(ifp == NULLIF){
		printf("Interface \"%s\" unknown\n",argv[1]);
		return 1;
	}
	if(argc < 3){
		printf("%s: %s\n",ifp->name,
		 (ifp->flags & CONNECT_MODE) ? "VC mode" : "Datagram mode");
		return 0;
	}
	switch(argv[2][0]){
	case 'v':
	case 'c':
	case 'V':
	case 'C':
		ifp->flags = CONNECT_MODE;
		break;
	case 'd':
	case 'D':
		ifp->flags = DATAGRAM_MODE;
		break;
	default:
		printf("Usage: %s [vc | datagram]\n",argv[0]);
		return 1;
	}
	return 0;
}

#ifdef SERVERS
int dostart(argc,argv)
int argc;
char *argv[];
{
#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif
	return subcmd(startcmds,argc,argv);
}
int dostop(argc,argv)
int argc;
char *argv[];
{
#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif
	return subcmd(stopcmds,argc,argv);
}
#endif /* SERVERS */

#ifdef  TRACE
/* Display the trace flags for a particular interface */
static void
showtrace(ifp)
	register struct interface *ifp;
{
#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif

	if(ifp == NULLIF)
		return;
	printf("%s:",ifp->name);
	if(ifp->trace & (IF_TRACE_IN | IF_TRACE_OUT)){
		if(ifp->trace & IF_TRACE_IN)
			printf(" input");
		if(ifp->trace & IF_TRACE_OUT)
			printf(" output");

		if(ifp->trace & IF_TRACE_HEX)
			printf(" (Hex/ASCII dump)");
		else if(ifp->trace & IF_TRACE_ASCII)
			printf(" (ASCII dump)");
		else
			printf(" (headers only)");
		printf("\n");
	} else
		printf(" tracing off\n");
	fflush(stdout);
}


int
dotrace(argc,argv)
int argc;
char *argv[];
{
	extern int notraceall;  /* trace only in command mode? */
	struct interface *ifp;
	int htoi();

#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif
	if(argc < 2){
		printf("trace mode is %s\n", (notraceall ? "cmdmode" : "allmode"));
		printf("trace to %s\n",trfp == stdout? "console" : trname);
		showtrace(&loopback);
		for(ifp = ifaces; ifp != NULLIF; ifp = ifp->next)
			showtrace(ifp);
		return 0;
	}
	if(strcmp("to",argv[1]) == 0){
		if(argc >= 3){
			if(trfp != stdout)
			        fclose(trfp);
			if(strncmp(argv[2],"con",3) == 0)
			        trfp = stdout;
			else {
				if((trfp = fopen(argv[2],"a")) == NULLFILE)
				{
					printf("%s: cannot open\n",argv[2]);
					trfp = stdout;
					return 1;
				}
			}
			strcpy(trname,argv[2]);
		} else {
			printf("trace to %s\n",trfp == stdout? "console" : trname);
		}
		return 0;
	}
	if(strcmp("loopback",argv[1]) == 0)
		ifp = &loopback;
	else if (strcmp("cmdmode", argv[1]) == 0) {
		notraceall = 1;
		return 0;
	} else if (strcmp("allmode", argv[1]) == 0) {
		notraceall = 0;
		return 0;
	} else
		for(ifp = ifaces; ifp != NULLIF; ifp = ifp->next)
			if(strcmp(ifp->name,argv[1]) == 0)
				break;

	if(ifp == NULLIF){
		printf("Interface %s unknown\n",argv[1]);
		return 1;
	}
	if(argc >= 3)
		ifp->trace = htoi(argv[2]);

	showtrace(ifp);
	return 0;
}

#endif /* TRACE */



int
doescape(argc,argv)
int argc;
char *argv[];
{
#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif
	if(argc < 2)
		printf("0x%x\n",escape);
	else
		escape = *argv[1];
	return 0;
}



int doremote(argc,argv)
int argc;
char *argv[];
{
	struct socket fsock,lsock;
	struct mbuf *bp;
	void send_udp();
	extern int atoi();
	argc = argc;
#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif
	lsock.address = ip_addr;
	fsock.address = resolve(argv[1]);
	lsock.port = fsock.port = atoi(argv[2]);
	bp = alloc_mbuf(1);
	if(strcmp(argv[3],"reset") == 0)
		*bp->data = SYS_RESET;	/* only if enabled in pc.c */
							/* in Unix, this will exit(2) */
	else if(strcmp(argv[3],"exit") == 0)
		*bp->data = SYS_EXIT;		/* will exit(1) which will distinguish */
	else {                        /* from a normal exit */
		printf("Unknown command %s\n",argv[3]);
		return 1;
	}
	bp->cnt = 1;
	send_udp(&lsock,&fsock,0,0,bp,0,0,0);
	return 0;
}

void
keep_things_going()
{
	void check_time();
	struct interface *ifp;
	struct mbuf *bp;

#if defined(UNIX) && defined(TELUNIX)
	void tnix_scan();
#endif

#ifdef TRACE
	void dump();
#endif

#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif
	/* Service the loopback queue */
	if((bp = dequeue(&loopq)) != NULLBUF){
		struct ip ip;
#ifdef  TRACE
		dump(&loopback,IF_TRACE_IN,TRACE_IP,bp);
#endif
		/* Extract IP header */
		ntohip(&ip,&bp);
		ip_recv(&ip,bp,0);
	}
	/* Service the interfaces */

	do {
		io_active = 0;
		for(ifp = ifaces; ifp != NULLIF; ifp = ifp->next){
			if(ifp->recv != NULLVFP)
				(*ifp->recv)(ifp);
		}
	} while(io_active);

	/* Service the clock if it has ticked */
	check_time();

#if defined(UNIX) && defined(TELUNIX)
	tnix_scan();	/* this is what eihalt() calls */
#endif
}

	/* Process any keyboard input - removed from main to make accessable */
	/* by other processes - K5JB */

void
check_kbd()
{
	char *ttybuf;
	int c;
	int16 cnt;
	int ttydriv(),cmdparse(),kbread();

#ifdef  FLOW
	extern int ttyflow;
#endif
#ifdef COHPROF
profile(__LINE__,__FILE__);
#endif


	if(reportquit_flag)	/* set by quit signal and report is deferred */
		report_quit();		/* until now */
	if(backgrd)		/* is running in background of shl */
		return;

	while((background == 0) && ((c = kbread()) != -1))
	{


		if(c == escape && escape != 0){
			if(mode != CMD_MODE){
				printf("\r\n");
				cmdmode();
			}
			continue;
		}


#ifndef FLOW
		if ((cnt = ttydriv(c, &ttybuf)) == 0)
			continue;
#else
		cnt = ttydriv(c, &ttybuf);
		if (ttyflow && (mode != CMD_MODE))
			go();           /* display pending chars */
		if (cnt == 0)
			continue;
#endif  /* FLOW */


		if((ttybuf[0] == escape) && (escape != 0)) {
			if(mode != CMD_MODE){
				printf("\r\n");
				cmdmode();
			}
			continue;
		}


		switch(mode){
			case CMD_MODE:
				(void)cmdparse(cmds,ttybuf);
				fflush(stdout);
			break;
			case CONV_MODE:


				if(ttybuf[0] == escape && escape != 0){
					printf("\n");
					cmdmode();
				} else

				if(current->parse != NULLFP)
					(*current->parse)(ttybuf,cnt);
			break;
		}
		if(mode == CMD_MODE){
			printf(prompt);
			fflush(stdout);
		}
	}  /* while */
}

int
main(argc,argv)
int argc;
char *argv[];
{
	static char inbuf[BUFSIZ];      /* keep it off the stack */
	char *fgets();
	int cmdparse(), revolve();
	void check_time(),ip_recv(),ioinit(), initpistons();
	FILE *fp;
	char *getenv();
	int system(), mxinit();
	extern struct pollfd piston[];
	argc = argc;

#ifdef COHPROF
open_profile();
profile(__LINE__,__FILE__);
#endif

	if( (signal(SIGINT, SIG_IGN) == SIG_IGN) ) {
		system( "echo \"background\" > /usr/net/stat" );
		background++;
	} else 
		ioinit();
		if (!background) {
			printf("    Coherent User Level Internet Package (cohulip)\n" );
			printf("                   derived from\n" );
			printf(" Internet Protocol Package, (C) 1988 by Phil Karn, KA9Q\n");
			printf("     with contributed code from many other sources\n\n" );

			version();  /* do formatting in version.c */

			/* version will include options like Unix, DRSI, MULPORT, etc. */
		}

		fflush(stdout);
		sessions = (struct session *)calloc(nsessions,sizeof(struct session));
		fileinit(argv[1]);	/* added to permit environmental control K5JB */
		printf( "startup: [%s]\n", startup );
		fp = fopen(startup,"r");	/* and permit alternative startup files */
		if(fp != NULLFILE){

			while(fgets(inbuf,BUFSIZ,fp) != NULLCHAR) {
				cmdparse(cmds,inbuf);
			}
			fclose(fp);
		}
		cmdmode();
#ifdef USEMX
	mxinit();
#endif
		/* Main commutator loop, does not return from revolve */
		for(;;) {
		revolve();
		}
}
