/* Main network program - provides both client and server functions */

#define HOSTNAMELEN 32		/* changed from 16 by Bdale 860812 */

extern char STARTUP[];		/* File to read startup commands from */
extern char ONEXIT[];		/* File to read termination commands from */

#include <stdio.h>

#ifdef ATARI_ST
# ifdef MWC
#  include <osbind.h>
#  include <basepage.h>
# endif
# ifdef __TURBOC__
#  include <tos.h>
extern char *_BasPag;		/* base page pointer */
#  define BP	*_BasPag
# endif
#endif

#include "global.h"
#include "config.h"
#include "mbuf.h"
#include "buffers.h"
#include "netuser.h"
#include "timer.h"
#include "icmp.h"
#include "iface.h"
#include "ip.h"
#include "tcp.h"
#include "ax25.h"
#include "lapb.h"
#include "ftp.h"
#include "telnet.h"
#include "remote.h"
#ifdef	SCREEN
#include "screen.h"
#endif
#include "session.h"
#include "cmdparse.h"
#include "kiss.h"
#include "slip.h"
#include "tnc2.h"

#ifdef	ASY
#include "asy.h"
#endif

#ifdef	NRS
#include "nrs.h"
#endif

#ifdef	UNIX		/* BSD or SYS5 */
#include <termio.h>
#include "unix.h"
#endif

#ifdef	AMIGA
#include "amiga.h"
#endif

#ifdef	MAC
#include "mac.h"
#endif

#ifdef	CPM
#include "cpm.h"
#endif

#ifdef	TRACE
#include "trace.h"
/* Dummy structure for loopback tracing */
struct interface loopback = { NULLIF, "loopback" };
#endif

extern char version[];
extern int vercheck;
extern struct mbuf *loopq;
extern struct mbuf *axloopq;
extern FILE *trfp;
extern char trname[];

int flowctrl = 0;
int mode;
FILE *logfp;
char logname[40];
long starttime;
char badhost[] = "Unknown host \"%s\"\n";
char cantopen[] = "%s: cannot open\n";
char hostname[HOSTNAMELEN];
unsigned nsessions = NSESSIONS;
int32 resolve();
int16 lport = 1001;
char prompt[] = "net> ";
char nospace[] = "No space!!\n";	/* Generic malloc fail message */

static void log_finish();

#if	(!defined(MSDOS) && !defined(ATARI_ST)) /* PC/Atari uses F-10 key */
static int escape = 0x1d;		/* default escape character is ^] */
#else
static int escape = -2;			/* this corrensponds to F10 key */
#endif

/* Command lookup and branch table */
int go(),doax25(),cmdmode(),doconnect(),dotelnet(),doexit(),doclose(),
	dohostname(),doreset(),dotcp(),dotrace(),doescape(),dohelp(),dohost(),
	doroute(),doecho(),doflow(),dolog(),doip(),doetherstat(),
	memstat(),doarp(),dosession(),doftp(),dostart(),dostop(),doattach(),
	dosmtp(),doudp(),doparam(),doeol(),dohapnstat(),dobuffers(),
	doegstat(),dodump(),dorecord(),doupload(),dokick(),domode(),doshell(),
	dodate(),dodelete(),dodir(),docd(),docopy(),dorename(),dormdir(),domkdir(),
	dotype(),doatstat(),doping(),dosource(),domheard(),dofinger(),
	domorom(),donrstat(),dosccstat(),doauxcon(),doforward(),doremote(),
	doscreen(),dosetenv(),dowatchdog(),doat(),dolatency();

struct cmds cmds[] = {
	/* The "go" command must be first */
	"",		go,		0, NULLCHAR,	NULLCHAR,
#ifndef CPM
	"!",		doshell,	0, NULLCHAR,	"cannot run shell",
#endif
#if	(defined(MAC) && defined(APPLETALK))
	"applestat",	doatstat,	0, NULLCHAR,	NULLCHAR,
#endif
#if	(defined(IP) && (defined(AX25) || defined(ETHER) || defined(APPLETALK)))
	"arp",		doarp,		0, NULLCHAR,	NULLCHAR,
#endif
#if (!defined(MSDOS) || defined(MSC) || defined(__TURBOC__))
	"at",		doat,		0, NULLCHAR,	NULLCHAR,
#endif
	"attach",	doattach,	2,
		"<hardware> <hw specific options>", NULLCHAR,
#if	(defined(ATARI_ST) && defined(MWC))
	"auxcon",	doauxcon,	2, "<io code> [speed [7|8 [noflow]]]", NULLCHAR,
#endif
#ifdef	AX25
	"ax25",		doax25,		0, NULLCHAR,	NULLCHAR,
#endif
#ifdef	SCC
	"buffers",	dobuffers,	0, NULLCHAR,	NULLCHAR,
#endif
/* This one is out of alpabetical order to allow abbreviation to "c" */
#ifdef	AX25
	"connect",	doconnect,	3, "<interface> <callsign> [digipeaters]",
		NULLCHAR,
#endif
#if	(!defined(CPM) && !defined(UNIX))   /* BSD or SYS5 */
	"cd",		docd,		0, NULLCHAR,	NULLCHAR,
#endif
	"close",	doclose,	0, NULLCHAR,	NULLCHAR,
#if	(!defined(CPM) && !defined(UNIX))   /* BSD or SYS5 */
	"copy",		docopy,		3, "<file> ... <dest>", NULLCHAR,
	"date",		dodate,		0, NULLCHAR,	"Usage: date [mmddhhmmss[yy]]",
	"delete",	dodelete,	2, "[-f] <file> ...", NULLCHAR,
	"dir",		dodir,		0, NULLCHAR,	NULLCHAR,
#endif
	"disconnect",	doclose,	0, NULLCHAR,	NULLCHAR,
#ifdef	EAGLE
	"eaglestat",	doegstat,	0, NULLCHAR,	NULLCHAR,
#endif
#ifdef	IP
	"echo",		doecho,		0, NULLCHAR,	"echo options: refuse accept",
	"eol",		doeol,		0, NULLCHAR,	"eol options: unix standard",
#endif
	"escape",	doescape,	0, NULLCHAR,	NULLCHAR,
#ifdef	PC_EC
	"etherstat",	doetherstat,	0, NULLCHAR,	NULLCHAR,
#endif
	"exit",		doexit,		0, NULLCHAR,	NULLCHAR,
#ifdef	IP
	"finger",	dofinger,	2, "[<user>][@<host>]",
					"Usage: finger <user> | <user>@<host> | @<host>",
#endif
	"flow",		doflow,		0, NULLCHAR,	NULLCHAR,
	"forward",	doforward,	0, NULLCHAR,	NULLCHAR,
#if	(defined(IP) && defined(FTPC))
	"ftp",		doftp,		2, "<address> [<port> [<tos>]]",	    NULLCHAR,
#endif
#ifdef	HAPN
	"hapnstat",	dohapnstat,	0, NULLCHAR,	NULLCHAR,
#endif
	"help",		dohelp,		0, NULLCHAR,	NULLCHAR,
#ifdef	IP
	"host",		dohost,		2, "<hostname>|[ip_address]",	NULLCHAR,
	"hostname",	dohostname,	0, NULLCHAR,	NULLCHAR,
	"ip",		doip,		0, NULLCHAR,	NULLCHAR,
#endif
	"kick",		dokick,		0, NULLCHAR,	NULLCHAR,
#ifdef	ATARI_ST
	"latency",	dolatency,	2, "yes|no|raw|stat", "Usage: latency yes|no|raw|stat",
#endif
#ifdef	SERVERS
	"log",		dolog,		0, NULLCHAR,	NULLCHAR,
#endif
#if	(defined(MSDOS) || defined(ATARI_ST) || defined(UNIX))
	"memstat",	memstat,	0, NULLCHAR,	NULLCHAR,
#endif
#ifdef	MHEARD
	"mheard",	domheard,	2, "<interface> [<maxheard>|clear]", NULLCHAR,
#endif
#if	(!defined(CPM) && !defined(UNIX))   /* BSD or SYS5 */
	"mkdir",	domkdir,	2, "[-f] <dir> ...", NULLCHAR,
#endif
#if (defined(IP) && defined(AX25))
	"mode",		domode,		2, "<interface>",  NULLCHAR,
#endif
#ifdef	NETROM
	"morom",	domorom,	0, NULLCHAR,	NULLCHAR,
#endif
#ifdef	NRS
	"nrstat",	donrstat,	0, NULLCHAR,	NULLCHAR,
#endif
	"param",	doparam,	2, "<interface>", NULLCHAR,
#ifdef	IP
	"ping",		doping,		0, NULLCHAR,	NULLCHAR,
#endif
#if	(!defined(CPM) && !defined(UNIX))   /* BSD or SYS5 */
	"pwd",		docd,		0, NULLCHAR,	NULLCHAR,
#endif
	"record",	dorecord,	0, NULLCHAR,	NULLCHAR,
#ifdef	IP
	"remote",	doremote,	4, "<address> <port> <command>",
							NULLCHAR,
#endif
#if	(!defined(CPM) && !defined(UNIX))   /* BSD or SYS5 */
	"rename",	dorename,	3, "<oldname> <newname>", NULLCHAR,
#endif
	"reset",	doreset,	0, NULLCHAR,	NULLCHAR,
#if	(!defined(CPM) && !defined(UNIX))   /* BSD or SYS5 */
	"rmdir",	dormdir,	2, "[-f] <dir> ...", NULLCHAR,
#endif
#ifdef	IP
	"route",	doroute,	0, NULLCHAR,	NULLCHAR,
#endif
/* This one is out of alpabetical order to allow abbreviation to "s" */
	"session",	dosession,	0, NULLCHAR,	NULLCHAR,
#ifdef	SCC
	"sccstat",	dosccstat,	0, NULLCHAR,	NULLCHAR,
#endif
#if	(defined(ATARI_ST) && defined(MWC))
	"screen",	doscreen,	2, "bios|direct|tos", "screen options: bios direct tos",
#endif
	"setenv",	dosetenv,	0, NULLCHAR,	"setenv failed",
#ifndef CPM
	"shell",	doshell,	0, NULLCHAR,	"cannot run shell",
#endif
#if	(defined(IP) && defined(SMTPC))
	"smtp",		dosmtp,		0, NULLCHAR,	NULLCHAR,
#endif
	"source",	dosource,	2, "<file>", NULLCHAR,
#ifdef	SERVERS
	"start",	dostart,	2, "<servername>",NULLCHAR,
	"stop",		dostop,		2, "<servername>", NULLCHAR,
#endif
#ifdef	IP
	"tcp",		dotcp,		0, NULLCHAR,	NULLCHAR,
	"telnet",	dotelnet,	2, "<address> [<port> [<tos>]]",	 NULLCHAR,
#endif
#ifdef	TRACE
	"trace",	dotrace,	0, NULLCHAR,	NULLCHAR,
#endif
#if	(!defined(CPM) && !defined(UNIX))   /* BSD or SYS5 */
	"type",		dotype,		2, "<file> [<lines>]", NULLCHAR,
#endif
#ifdef	IP
	"udp",		doudp,		0, NULLCHAR,	NULLCHAR,
#endif
	"upload",	doupload,	0, NULLCHAR,	NULLCHAR,
#if	(defined(MSDOS) || defined(ATARI_ST))
	"watchdog",	dowatchdog,	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(),fing1(),ftp1(),smtp1(),tn1(),rem1(),rcmd1();
static struct cmds startcmds[] = {
	"discard",	dis1,		0, NULLCHAR, NULLCHAR,
	"echo",		echo1,		0, NULLCHAR, NULLCHAR,
	"finger",	fing1,		0, NULLCHAR, NULLCHAR,
	"ftp",		ftp1,		0, NULLCHAR, NULLCHAR,
	"smtp",		smtp1,		0, NULLCHAR, NULLCHAR,
	"telnet",	tn1,		0, NULLCHAR, NULLCHAR,
#ifdef RCMD
	"rcmd",		rcmd1,		0, NULLCHAR, NULLCHAR,
#endif
	"remote",	rem1,		0, NULLCHAR, NULLCHAR,
	NULLCHAR,	NULLFP,		0, "?options", NULLCHAR
};
int dis0(),echo0(),fing0(),ftp0(),smtp0(),tn0(),rem0(),rcmd0();
static struct cmds stopcmds[] = {
	"discard",	dis0,		0, NULLCHAR, NULLCHAR,
	"echo",		echo0,		0, NULLCHAR, NULLCHAR,
	"finger",	fing0,		0, NULLCHAR, NULLCHAR,
	"ftp",		ftp0,		0, NULLCHAR, NULLCHAR,
	"smtp",		smtp0,		0, NULLCHAR, NULLCHAR,
	"telnet",	tn0,		0, NULLCHAR, NULLCHAR,
#ifdef RCMD
	"rcmd",		rcmd0,		0, NULLCHAR, NULLCHAR,
#endif
	"remote",	rem0,		0, NULLCHAR, NULLCHAR,
	NULLCHAR,	NULLFP,		0, "?options", NULLCHAR
};
#endif

main(argc,argv,envp)
int argc;
char *argv[];
char *envp[];
{
	int c;
	char *ttybuf,*getnenv();
	int16 cnt,active;
	struct interface *ifp;
	struct mbuf *bp;
	int ttydriv();
	int cmdparse();
	int kbread();
	void check_time(),ip_recv();

	readenv(envp);				/* read environment variables */
	/* any name=value before first filename processed before ioinit() */
	while(argc > 1 && index(argv[1],'=') != NULLCHAR) {
		dosetenv(2,argv++);		/* process name=value strings */
		argc--;				/* and remove them from cmdline */
	}

	ioinit();
#ifdef	MSDOS
	chktasker();
#endif
#ifdef	SCREEN
	if (InitTerm() < 0){
		iostop();
		exit(1);
	}
#endif
#ifdef	IP
	printf("KA9Q Internet Protocol Package, v%s\n",version);
#else
	printf("KA9Q AX.25 Package, v%s\n",version);
#endif
	printf("Copyright 1988 by Phil Karn, KA9Q\n");
	fflush(stdout);
	time(&starttime);			/* record startuptime */
	sessions = (struct session *)calloc(nsessions,sizeof(struct session));
	if(argc > 1){
		while (argc-- > 1)		/* still args left */
			if (index(argv[1],'=') == NULLCHAR)
				/* Read startupfile named on command line */
				dosource(2,argv++);
			else
				/* Process yet another env assignment */
				dosetenv(2,argv++);
	} else {
		/* read default startup file */
		char *args[2];
		args[1] = getnenv(STARTUP);
		dosource(2,args);
	}

	/* Unfortunately it turns out to be necessary to verify that the
	 * the version string is still correct...
	 */
	for (ttybuf = version - 123; ttybuf[123] && ttybuf[123] != ' '; ttybuf++)
		vercheck -= ttybuf[123];
	while (vercheck)
		;

	cmdmode();

	/* Main commutator loop */
	for(;;){
		/* Clear the "active" flag so we can see if we have to sleep */
		active = 0;

		/* Process any keyboard input */
		while((c = kbread()) != -1){
			active = 1;

			if(c == escape && escape != 0){ /* command mode escape? */
				if(mode != CMD_MODE){
					printf("\n");
					cmdmode();
				}
				continue;
			}
			if((cnt = ttydriv(c,&ttybuf)) == 0)
				continue;
			switch(mode){
			case CMD_MODE:
				(void)cmdparse(cmds,ttybuf);
				fflush(stdout);
				break;
			case CONV_MODE:
#if	(!defined(MSDOS) && !defined(ATARI_ST))
				if(ttybuf[0] == escape && escape != 0){
					printf("\n");
					cmdmode();
					break;
				}
#endif
				if(current->parse != NULLFP)
					(*current->parse)(ttybuf,cnt);
				break;
			}
			if(mode == CMD_MODE){
				printf(prompt);
				fflush(stdout);
			}
		}
		if(mode == CONV_MODE && ttydriv(-1,NULL) == 0)
			go();
#ifdef	IP
		/* Service the IP 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);

			active++;
		}
#endif
#ifdef	AX25
		/* Service the AX.25 loopback queue */
		if((bp = dequeue(&axloopq)) != NULLBUF){
			ifp = (struct interface *) pull32(&bp); /* interface it came from */
#ifdef	TRACE
			dump(&loopback,IF_TRACE_IN,TRACE_AX25,bp);
#endif
			ax_recv(ifp,bp);

			active++;
		}
#endif
		/* Service the interfaces */
		for(ifp = ifaces; ifp != NULLIF; ifp = ifp->next){
			if(ifp->recv != NULLFP)
				active += (*ifp->recv)(ifp);
		}
#ifdef	TNC2
		/* Service the TNC2S */
		{
			struct tnc *tnc2;

			for (tnc2 = tnc2s; tnc2 != NULLTNC; tnc2 = tnc2->next)
				tnc2_serv(tnc2);
		}
#endif
#ifdef	SCC
		/* Get new buffers for receive interrupt handler */
		buf_alloc();
#endif

		/* Service the clock if it has ticked */
		check_time();

		if (!active) {
#ifdef	MSDOS
			/* Tell DoubleDos to let the other task run for awhile.
			 * If DoubleDos isn't active, this is a no-op
			 */
			giveup();
#else
			/* Wait until interrupt, then do it all over again */
			eihalt();
#endif
		}
	}
}
/* Standard commands called from main */

/* Enter command mode */
int
cmdmode()
{
	if(mode != CMD_MODE){
		mode = CMD_MODE;
		cooked();
		printf(prompt);
		fflush(stdout);
	}
	return 0;
}
static
dosource(argc,argv)
int argc;
char *argv[];
{
	static char inbuf[200],savebuf[200];	/* keep it off the stack */
	char filename[80];			/* for error messages */
	int linenum = 0;
	FILE *fp;

	if((fp = fopen(argv[1],"r")) == NULLFILE){
		printf(cantopen,argv[1]);
		return 1;
	}

	strcpy(filename,argv[1]);

	while(fgets(inbuf,sizeof(inbuf),fp) != NULLCHAR){
		rip(inbuf);
		strcpy(savebuf,inbuf);
		linenum++;
		if(cmdparse(cmds,inbuf) != 0)
			printf("*** file \"%s\", line %d: %s\n",
				filename,linenum,savebuf);
	}

	fclose(fp);
	return 0;
}
doexit(argc,argv)
int argc;
char *argv[];
{
	register int i;
	int noexit = 0;
	char *args[2];
#ifdef	IP
	struct tcb *tcb;
#endif
#ifdef	TNC2
	struct tnc *tnc2;
#endif

	if(argc > 0 && (argc < 2 || argv[1][0] != '!')){
#ifdef	AX25
		for(i = 0; i < NHASH; i++)
			if(ax25_cb[i] != NULLAX25){
				printf("AX.25 connection(s) present\n");
				noexit++;
				break;
			}
#endif
#ifdef	IP
		for(i = 0; i < NTCB; i++)
			for(tcb = tcbs[i]; tcb != NULLTCB; tcb = tcb->next)
				if (!(tcb->state == LISTEN && (tcb->flags & CLONE)) &&
				    !(tcb->state == TIME_WAIT)){
					printf("TCP connection(s) present\n");
					noexit++;
					goto stopsearch;
				}
stopsearch:
#endif
		if(noexit){
			printf("Use \"exit !\" to exit\n");
			return 1;
		}
	}

#ifdef IP
	/* neatly reset any open TCP sessions to allow users to clean up */
	/* notably, SMTP will remove it's lockfiles. */

	for(i = 0; i < NTCB; i++)
		for(tcb = tcbs[i]; tcb != NULLTCB; tcb = tcb->next)
			if (!(tcb->state == LISTEN && (tcb->flags & CLONE)))
				close_self(tcb,RESET);
#endif
#ifdef	AX25
	/* reset AX.25 sessions because recording files may be open */
	for(i = 0; i < NHASH; i++)
		while(ax25_cb[i] != NULLAX25)
			reset_ax25(ax25_cb[i]);
#endif
#ifdef	TNC2
	/* Stop the TNC2S (they change an interrupt vector) */
	while ((tnc2 = tnc2s) != NULLTNC) {
	    tnc2s = tnc2->next;
	    tnc2_term(tnc2);
	}
#endif

#ifdef	SERVERS
	if(logfp != NULLFILE){
		fclose(logfp);
		logfp = NULLFILE;
	}
#endif
#ifdef	TRACE
	if(trfp != stdout){
		fclose(trfp);
		trfp = stdout;
	}
#endif

	/* execute onexit command file */
	args[1] = getnenv(ONEXIT);
	dosource(2,args);

#ifdef	SCREEN
	FinitTerm();
#endif
	iostop();
#ifdef	SCC
	sccstop();
#endif
	if (argc >= 0)			/* REMOTE RESET uses -1 */
		exit(0);
	return -1;
}
#ifdef	IP
static
dohost(argc,argv)
int argc;
char *argv[];
{
	int32 ip_address;

	if ((ip_address = resolve(argv[1])) != 0){
		printf("%s is %s\n",
			(argv[1][0] == '[')? inet_n2h(ip_address) : argv[1],
			inet_ntoa(ip_address));
		return 0;
	}

	printf(badhost,argv[1]);
	return 1;
}
static
dohostname(argc,argv)
int argc;
char *argv[];
{
	char *strncpy();

	if(argc < 2)
		printf("%s\n",hostname);
	else
		strncpy(hostname,argv[1],HOSTNAMELEN);
	return 0;
}
#endif
#ifdef	SERVERS
static
int
dolog(argc,argv)
int argc;
char *argv[];
{
	char *strncpy();

	if(argc < 2){
		if(logfp != NULLFILE)
			printf("Logging to %s\n",logname);
		else
			printf("Logging off\n");
		return 0;
	}
	if(logfp != NULLFILE){
#ifdef ATARI_ST
		if(!strcmp(argv[1],"proc")){
			/* "log proc": log the proc exception area & reset cause */
			char *save_ssp = (char *) Super(NULL);
			register long *proc = (long *) 0x380;
			int i;
			long tloc;
#ifdef MWC
			extern long _boottime;
#endif

			if (*proc == 0x12345678L){
				*proc++ = 0;
				log_msg("exception dump (BP=%08lx)",BP);

				for (i = 1; i < 27; i++) {
					fprintf(logfp," %08lx",*proc++);
					if ((i % 8) == 0)
						fprintf(logfp,"\n");
				}
				log_finish();
			}

			proc = (long *) 0x3fc;
			if (*proc == 0x4e455421L || *proc == 0x53574154L){
				time(&tloc);
				fprintf(logfp,"%.24s RESET %s",
					ctime(&tloc),(*proc == 0x4e455421L)?
					"while NET was running" : "by software watchdog");
#ifdef MWC
				if ((tloc - _boottime) < 120L)
					fprintf(logfp," (coldboot)");
#endif
				log_finish();
			}
			*proc = 0x4e455421L;		/* set signature */

			Super(save_ssp);
			return 0;
		}
#endif
		fclose(logfp);
		logfp = NULLFILE;
	}
	if(strcmp(argv[1],"off") && strcmp(argv[1],"stop")){
		strncpy(logname,argv[1],sizeof(logname) - 1);
		logfp = fopen(logname,"a");
		if(argc > 2)
			log_msg("%s",argv[2]);
	}
	return 0;
}
#endif

static
int
dohelp(argc,argv)
int argc;
char *argv[];
{
	register struct cmds *cmdp;
	int i,j;

	printf("Main commands:\n");
	for(i=0,cmdp = cmds;cmdp->name != NULL;cmdp++,i++){
		printf("%s",cmdp->name);
		if((i % 4) == 3)
			printf("\n");
		else {
			for(j=strlen(cmdp->name);j < 16; j++)
				putchar(' ');
		}
	}
	if((i % 4) != 0)
		printf("\n");
	return 0;
}

#ifdef IP
doecho(argc,argv)
int argc;
char *argv[];
{
	extern int refuse_echo;

	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;
}
/* set for unix end of line for remote echo mode telnet */
doeol(argc,argv)
int argc;
char *argv[];
{
	extern int unix_line_mode;

	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
doflow(argc,argv)
int argc;
char *argv[];
{
	if(argc < 2){
		printf("flow %s\n",flowctrl?"on":"off");
	} else {
		if(strcmp(argv[1],"on") == 0)
			flowctrl = 1;
		else
			flowctrl = 0;
	}
	return 0;
}
/* Attach an interface
 * Syntax: attach <hw type> <I/O address> <vector> <mode> <label> <bufsize> [<MTU>] [<speed>] [<call>]
 */
doattach(argc,argv)
int argc;
char *argv[];
{
	extern struct cmds attab[];

	return subcmd(attab,argc,argv);
}
/* Manipulate I/O device parameters */
doparam(argc,argv)
int argc;
char *argv[];
{
	register struct interface *ifp;

	if((ifp = ifunit(argv[1])) == NULLIF){
		return 1;
	}
	if(ifp->ioctl == NULLFP){
		printf("Not supported\n");
		return 1;
	}
	/* Pass rest of args to device-specific code */
	return (*ifp->ioctl)(ifp,argc-2,argv+2);
}

#if	(defined(IP) && defined(SERVERS))
/* Log messages of the form
 * Tue Jan 31 00:00:00 1987 44.64.0.7:1003 open FTP
 */
/*VARARGS2*/
log_tcp(tcb,fmt,arg1,arg2,arg3,arg4)
struct tcb *tcb;
char *fmt;
char *arg1,*arg2,*arg3,*arg4;
{
	long t;

	if(logfp == NULLFILE)
		return;
	time(&t);
	fprintf(logfp,"%.24s %s - ",ctime(&t),psocket(&tcb->conn.remote));
	fprintf(logfp,fmt,arg1,arg2,arg3,arg4);
	log_finish();
}
#endif
#ifdef	AX25
/* Log messages of the form
 * Tue Jan 31 00:00:00 1987 (ax0) PE1CHL->PA0GRI Connected
 */
/*VARARGS2*/
log_ax(axp,fmt,arg1,arg2,arg3,arg4)
struct ax25_cb *axp;
char *fmt;
char *arg1,*arg2,*arg3,*arg4;
{
# ifdef SERVERS
	long t;
	char tmp1[10],tmp2[10];

	if(logfp == NULLFILE)
		return;
	time(&t);
	pax25(tmp1,&axp->addr.source);
	pax25(tmp2,&axp->addr.dest);
	fprintf(logfp,"%.24s (%s) %s->%s - ",ctime(&t),axp->interface->name,tmp1,tmp2);
	fprintf(logfp,fmt,arg1,arg2,arg3,arg4);
	log_finish();
# endif
}
#endif
/* Log messages of the form
 * Tue Jan 31 00:00:00 1987 started
 */
/*VARARGS1*/
log_msg(fmt,arg1,arg2,arg3,arg4)
char *fmt;
char *arg1,*arg2,*arg3,*arg4;
{
#ifdef	SERVERS
	long t;

	if(logfp == NULLFILE)
		return;
	time(&t);
	fprintf(logfp,"%.24s ",ctime(&t));
	fprintf(logfp,fmt,arg1,arg2,arg3,arg4);
	log_finish();
#endif
}
#ifdef	SERVERS
/* put linefeed and flush logfile */
static void
log_finish ()
{
	fprintf(logfp,"\n");
	if(ferror(logfp)){
		fclose(logfp);
		logfp = NULLFILE;
		printf("Error on %s - logging off\n",logname);
		return;
	}
# if (defined(MSDOS) || defined(ATARI_ST))
	/* MS-DOS doesn't really flush files until they're closed */
	fclose(logfp);
	logfp = fopen(logname,"a");
# else
	fflush(logfp);
# endif
}
#endif
/* Configuration-dependent code */

/* List of supported hardware devices */
int ec_attach(),asy_attach(),kiss_attach(),pc_attach(),eg_attach(),
	hapn_attach(),at_attach(),nr_attach(),pk_attach(),scc_attach();

struct cmds attab[] = {
#ifdef	PC_EC
	/* 3-Com Ethernet interface */
	"3c500", ec_attach, 7,
	"<address> <vector> arpa <label> <buffers> <mtu>",
	"Could not attach 3c500",
#endif
#if	(defined(ASY) && !defined(COMBIOS))
	/* Ordinary PC asynchronous adaptor */
	"asy", asy_attach, 8,
# ifdef NRS
	"<addr> <vect> slip|ax25|nrs <label> <rxbuf> <mtu> <speed> [<call>]",
# else
	"<addr> <vect> slip|ax25 <label> <rxbuf> <mtu> <speed> [<call>]",
# endif
	"Could not attach asy",
#endif
#if	(defined(ASY) && defined(COMBIOS))
	/* PC asynchronous adaptor interfaced using COMBIOS */
	"com", asy_attach, 7,
# ifdef NRS
	"<number> slip|ax25|nrs <label> <mtu> <speed> <handshake> [<call>]",
# else
	"<number> slip|ax25 <label> <mtu> <speed> <handshake> [<call>]",
# endif
	"Could not attach COM",
#endif
#ifdef	PC100
	/* PACCOMM PC-100 8530 HDLC adaptor */
	"pc100", pc_attach, 9,
	"<addr> <vect> ax25 <label> <rxbuf> <mtu> <speed> <call>",
	"Could not attach pc100",
#endif
#ifdef	EAGLE
	/* EAGLE RS-232C 8530 HDLC adaptor */
	"eagle", eg_attach, 9,
	"<addr> <vect> ax25 <label> <rxbuf> <mtu> <speed> <call>",
	"Could not attach eagle",
#endif
#ifdef	HAPN
	/* Hamilton Area Packet Radio (HAPN) 8273 HDLC adaptor */
	"hapn", hapn_attach, 9,
	"<addr> <vect> ax25 <label> <rxbuf> <mtu> csma|full <call>",
	"Could not attach hapn",
#endif
#if	((defined(ASY) && defined(AX25)) || defined(SCC))
	/* Secundary KISS channels for multi-channel KISS TNC */
	"kiss", kiss_attach, 6,
	"<parent> <chan> <label> <mtu> <call>",
	"Could not attach KISS",
#endif
#ifdef	APPLETALK
	/* Macintosh AppleTalk */
	"0", at_attach, 7,
	"<protocol type> <device> arpa <label> <rx bufsize> <mtu>",
	"Could not attach Appletalk",
#endif
#ifdef	NETROM
	/* fake netrom interface */
	"morom", nr_attach, 1,
	"[<callsign>]",
	"Could not attach morom",
#endif
#ifdef	PACKET
	/* FTP Software's packet driver spec */
	"packet", pk_attach, 4,
	"<int#> <label> <buffers> <mtu>",
	"Could not attach packet driver",
#endif
#ifdef	SCC
	/* Generic Z8530 SCC adaptor - for SLIP,KISS or AX25 */
	"scc", scc_attach, 6,
	"<chips> init <base> <sp> <A> <B> <D> <ia> <iv> p|r<clk> [<o>]\n\
or:		  <num> slip|kiss|ax25 <label> <mtu> <speed> [<call>]",
	"Could not attach scc",
#endif
	NULLCHAR, NULLFP, 0,
	"?devices",
	NULLCHAR
};

/* Protocol tracing function pointers */
#ifdef	TRACE
int ax25_dump(),nettra_dump(),ether_dump(),ip_dump(),at_dump();

int (*tracef[])() = {
#ifdef	AX25
	ax25_dump,nettra_dump,
#else
	NULLFP,NULLFP,
#endif

#ifdef	ETHER
	ether_dump,
#else
	NULLFP,
#endif
	ip_dump,

#ifdef	APPLETALK
	at_dump,
#else
	NULLFP,
#endif
};
#else
int (*tracef[])() = { NULLFP }; /* No tracing at all */
dump(interface,direction,type,bp)
struct interface *interface;
int direction;
unsigned type;
struct mbuf *bp;
{
}
#endif

#ifdef	ASY
/* Attach a serial interface to the system
 * direct interface:
 * 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)
 * 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 (AX25/NRS only)
 * interface via COMBIOS:
 * argv[0]: hardware type, must be "com"
 * argv[1]: COM number (1 up)
 * argv[2]: mode, may be:
 *	    "slip" (point-to-point SLIP)
 *	    "ax25" (AX.25 frame format in SLIP for raw TNC)
 *	    "nrs" (NET/ROM format serial protocol)
 * argv[3]: interface label, e.g., "sl0"
 * argv[4]: maximum transmission unit, bytes
 * argv[5]: interface speed, e.g, "9600"
 * argv[6]: handshake type
 * argv[7]: optional ax.25 callsign (AX25/NRS only)
 */
# ifndef COMBIOS
#define ASY_MODE	3
#define ASY_LABEL	4
#define ASY_MTU		6
#define ASY_CALL	8
# else
#define ASY_MODE	2
#define ASY_LABEL	3
#define ASY_MTU		4
#define ASY_CALL	7
# endif
int
asy_attach(argc,argv)
int argc;
char *argv[];
{
	register struct interface *if_asy;
	extern struct interface *ifaces;
# ifdef AX25
	struct ax25_addr hwaddr;
	register struct ax25_call *axc;
# endif
	int16 dev;
	int mode;
	int asy_init(), asy_ioctl(), asy_stop(), ax_output(), ax_send(),
	    com_init(), com_ioctl(), com_stop(), doslip(), kiss_ioctl(),
	    kiss_raw(), kiss_recv(), nrs_raw(), nrs_recv(), slip_raw(),
	    slip_recv(), slip_send();
	unsigned combios();

# ifdef	 COMBIOS
	if ((dev = atoi(argv[1]) - 1) < 0 || dev >= ASY_MAX) {
		printf("COM number out of range (1-%d)\n",ASY_MAX);
		return -1;
	}
# else
	if(nasy >= ASY_MAX){
		printf("Too many asynch controllers\n");
		return -1;
	}
	dev = nasy++;
# endif

	if(strcmp(argv[ASY_MODE],"slip") == 0)
		mode = SLIP_MODE;
	else if(strcmp(argv[ASY_MODE],"ax25") == 0)
		mode = AX25_MODE;
	else if(strcmp(argv[ASY_MODE],"nrs") == 0)
		mode = NRS_MODE;
	else
		mode = UNKNOWN;

	/* Create interface structure and fill in details */
	if_asy = (struct interface *)calloc(1,sizeof(struct interface));
	if_asy->name = malloc((unsigned)strlen(argv[ASY_LABEL])+1);
	strcpy(if_asy->name,argv[ASY_LABEL]);
	if_asy->mtu = atoi(argv[ASY_MTU]);
	if_asy->dev = dev;
	if_asy->recv = doslip;
# ifdef COMBIOS
	if_asy->stop = com_stop;
# else
	if_asy->stop = asy_stop;
# endif

	switch(mode){
# ifdef	 SLIP
	case SLIP_MODE:
#  ifdef COMBIOS
		if_asy->ioctl = com_ioctl;
#  else
		if_asy->ioctl = asy_ioctl;
#  endif
		if_asy->send = slip_send;
		if_asy->output = NULLFP;	/* ARP isn't used */
		if_asy->raw = if_asy->slipraw = slip_raw;
		slip[dev].recv = slip_recv;
		break;
# endif
# ifdef	 AX25
	case AX25_MODE:	 /* Set up a SLIP link to use AX.25 */
		if (setcall(&hwaddr,argv[ASY_CALL]) < 0) /* callsign present? */
		{
			printf("Interface %s needs a callsign in ax25 mode\n",
				if_asy->name);
			free(if_asy->name);
			free((char *)if_asy);
#  ifndef COMBIOS
			nasy--;
#  endif
			return -1;
		}

		axarp();

		if_asy->ioctl = kiss_ioctl;
		if_asy->send = ax_send;
		if_asy->output = ax_output;
		if_asy->raw = kiss_raw;
		if_asy->slipraw = slip_raw;
		slip[dev].recv = kiss_recv;
		if_asy->flags |= IF_AX25;
		if(if_asy->hwaddr == NULLCHAR)
			if((if_asy->hwaddr = malloc(sizeof(struct ax25_addr))) == NULLCHAR)
				return -1;

		memcpy(if_asy->hwaddr,(char *)&hwaddr,sizeof(struct ax25_addr));

		if((axc = cr_axcall(ax25_call,&hwaddr)) == NULLAXCALL)
			return -1;

		if(axc->interface != NULLIF)
			axc->flags |= MULTI_IF;
		axc->interface = if_asy;
		axc->mode = IP_ARP_CON;
		break;
# endif
# ifdef	 NRS
	case NRS_MODE: /* Set up a net/rom serial interface */
		if (setcall(&hwaddr,argv[ASY_CALL]) < 0) /* callsign present? */
		{
			printf("Interface %s needs a callsign in nrs mode\n",
				if_asy->name);
			free(if_asy->name);
			free((char *)if_asy);
#  ifndef COMBIOS
			nasy--;
#  endif
			return -1;
		}

		axarp();

		if_asy->recv = nrs_recv;
#  ifdef COMBIOS
		if_asy->ioctl = com_ioctl;
#  else
		if_asy->ioctl = asy_ioctl;
#  endif
		if_asy->send = ax_send;
		if_asy->output = ax_output;
		if_asy->raw = if_asy->slipraw = nrs_raw;
		nrs[dev].iface = if_asy;
		if_asy->flags |= IF_AX25;
		if(if_asy->hwaddr == NULLCHAR)
			if((if_asy->hwaddr = malloc(sizeof(struct ax25_addr))) == NULLCHAR)
				return -1;

		memcpy(if_asy->hwaddr,(char *)&hwaddr,sizeof(struct ax25_addr));

		if((axc = cr_axcall(ax25_call,&hwaddr)) == NULLAXCALL)
			return -1;

		if(axc->interface != NULLIF)
			axc->flags |= MULTI_IF;
		axc->interface = if_asy;
		axc->mode = IP_ARP_CON;
		break;
# endif
	default:
		printf("Mode %s unknown for interface %s\n",
			argv[ASY_MODE],argv[ASY_LABEL]);
		free(if_asy->name);
		free((char *)if_asy);
# ifndef COMBIOS
		nasy--;
# endif
		return -1;
	}
# ifdef	 COMBIOS
	if (com_init(dev) ||
	    com_speed(dev,atoi(argv[5]),argv[6])){
# else
	if (asy_init(dev,argv[1],argv[2],(unsigned)atoi(argv[5])) ||
	    asy_speed(dev,atoi(argv[7]))){
# endif
		free(if_asy->name);
		free((char *)if_asy);
# ifndef COMBIOS
		nasy--;
# endif
		return -1;
	}
	if_asy->next = ifaces;
	ifaces = if_asy;
	return 0;
}
#endif
#if ((defined(ASY) && defined(AX25)) || defined(SCC))
/* Attach a second channel on an existing KISS interface to the system
 * argv[0]: hardware type, must be "kiss"
 * argv[1]: parent KISS interface
 * argv[2]: channel number on KISS TNC (decimal, 1-15)
 * argv[3]: interface label, e.g., "ax0.1"
 * argv[4]: maximum transmission unit, bytes
 * argv[5]: ax.25 callsign
 */
int
kiss_attach(argc,argv)
int argc;
char *argv[];
{
	struct ax25_addr hwaddr;
	register struct interface *if_kiss,*if_asy;
	extern struct interface *ifaces;
	struct ax25_call *axc;
	int16 kisschan;
	int kiss_ioctl();

	/* Find the parent interface and validate it */
	if((if_asy = ifunit(argv[1])) == NULLIF){
		return -1;
	}
	if(if_asy->ioctl != kiss_ioctl || if_asy->kisschan != 0){
		printf("\"%s\" is not a KISS interface\n",argv[1]);
		return -1;
	}
	if((kisschan = atoi(argv[2])) < 1 || kisschan > 15){
		printf("KISS channel #%d invalid (range is 1-15)\n",kisschan);
		return -1;
	}
	if(if_asy->kiss == NULLKISS) {		/* first multi-channel add */
		if_asy->kiss = (struct kiss *)calloc(1,sizeof(struct kiss));
		if_asy->kiss->iface[0] = if_asy;
	}

	/* Create child interface structure and copy/fill details */
	if_kiss = (struct interface *)malloc(sizeof(struct interface));
	memcpy(if_kiss,if_asy,sizeof(struct interface));
	if_kiss->kisschan = kisschan;
	if_kiss->name = malloc((unsigned)strlen(argv[3])+1);
	strcpy(if_kiss->name,argv[3]);
	if_kiss->flags &= ~(IF_CONNECT_MODE);
	if_kiss->mtu = atoi(argv[4]);
	if_kiss->stop = NULLFP;
	if_kiss->nriface = NULL;
	if_kiss->heard = if_kiss->lheard = NULL;
	if_kiss->nheard = 0;

	/* Determine and set hardware address (callsign) */
	if (setcall(&hwaddr,argv[5]) < 0){	/* callsign valid? */
		printf("Invalid call for interface %s\n",if_kiss->name);
		free(if_kiss->name);
		free((char *)if_kiss);
		return -1;
	}

	if((if_kiss->hwaddr = malloc(sizeof(struct ax25_addr))) == NULLCHAR)
		return -1;

	memcpy(if_kiss->hwaddr,(char *)&hwaddr,sizeof(struct ax25_addr));

	/* Create axcall and set proper mode */
	if((axc = cr_axcall(ax25_call,&hwaddr)) == NULLAXCALL)
		return -1;

	if(axc->interface != NULLIF)
		axc->flags |= MULTI_IF;
	axc->interface = if_kiss;
	axc->mode = IP_ARP_CON;

	/* link it in the list of interfaces */
	if_kiss->next = ifaces;
	ifaces = if_kiss;
	if_asy->kiss->iface[kisschan] = if_kiss;
	return 0;
}
#endif

#ifndef NETROM
nr_route(axp,bp)
struct ax25_cb *axp;
struct mbuf *bp;
{
	free_p(bp);
}
nr_nodercv(ifp,src,bp)
struct interface *ifp;
struct ax25_addr *src;
struct mbuf *bp;
{
	free_p(bp);
}
# ifdef	 AX25
void
ax_incom(axp,cnt)
struct ax25_cb *axp;
int16 cnt;
{
	tnc_state(axp,DISCONNECTED,CONNECTED);	/* try interactive */
}
# endif
#endif

#ifndef TNC2
int
tnc2_kissrcv (interface,hdr,bp,kissrx)
struct interface *interface;
struct ax25 *hdr;
struct mbuf *bp;
int kissrx;

{
	return 0;
}
#endif

#if	(defined(IP) && defined(AX25))
/* Display or set IP interface control flags */
domode(argc,argv)
int argc;
char *argv[];
{
	register struct interface *ifp;

	if((ifp = ifunit(argv[1])) == NULLIF)
		return 1;
	if(argc < 3){
		printf("%s: %s\n",ifp->name,
		 (ifp->flags & IF_CONNECT_MODE) ? "VC mode" : "Datagram mode");
		return 0;
	}
	switch(argv[2][0]){
	case 'v':
	case 'c':
	case 'V':
	case 'C':
		ifp->flags |= IF_CONNECT_MODE;
		break;
	case 'd':
	case 'D':
		ifp->flags &= ~IF_CONNECT_MODE;
		break;
	default:
		printf("Usage: %s <interface> [vc | datagram]\n",argv[0]);
		return 1;
	}
	return 0;
}
#endif
#ifndef IP
/* dummy routines when IP not configured */
int
arp_input(interface,bp)
struct interface *interface;
struct mbuf *bp;
{
	free_p(bp);
	return -1;
}
arp_init(hwtype,hwalen,iptype,arptype,bdcst,format,scan)
unsigned int hwtype;	/* ARP Hardware type */
int hwalen;		/* Hardware address length */
int iptype;		/* Subnet's protocol ID for IP */
int arptype;		/* Subnet's protocol ID for ARP */
char *bdcst;		/* Subnet's broadcast address (if any) */
int (*format)();	/* Function to format hardware addresses */
int (*scan)();		/* Function to scan addresses in ascii */
{
	return 0;
}
char *
res_arp(interface,hardware,target,bp)
struct interface *interface;	/* Pointer to interface block */
int16 hardware;		/* Hardware type */
int32 target;		/* Target IP address */
struct mbuf *bp;	/* IP datagram to be queued if unresolved */
{
	free_p(bp);
	return NULLCHAR;
}
int
ip_route(bp,rxbroadcast)
struct mbuf *bp;
char rxbroadcast;
{
	free_p(bp);
	return -1;
}
void
tcp_dump(bpp,source,dest,check)
struct mbuf **bpp;
int32 source,dest;	/* IP source and dest addresses */
int check;		/* 0 if checksum test is to be bypassed */
{
}
void
udp_dump(bpp,source,dest,check)
struct mbuf **bpp;
int32 source,dest;
int check;		/* If 0, bypass checksum verify */
{
}
#endif

#ifdef	SERVERS
dostart(argc,argv)
int argc;
char *argv[];
{
	return subcmd(startcmds,argc,argv);
}
dostop(argc,argv)
int argc;
char *argv[];
{
	return subcmd(stopcmds,argc,argv);
}
#endif

#ifdef	TRACE
static
int
dotrace(argc,argv)
int argc;
char *argv[];
{
	struct interface *ifp;
	int htoi();

	if(argc < 2){
		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(cantopen,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((ifp = ifunit(argv[1])) == NULLIF)
			return 1;
	if(argc >= 3)
		ifp->trace = htoi(argv[2]);

	showtrace(ifp);
	return 0;
}
/* Display the trace flags for a particular interface */
static
showtrace(ifp)
register struct interface *ifp;
{
#ifdef DEBUG
	if(ifp == NULLIF)
		return;
#endif
	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");
}
#endif

static
int
doescape(argc,argv)
int argc;
char *argv[];
{
	if(argc < 2){
		if (escape < 0)
			printf("F%d\n",escape + 12);
		else
			printf("0x%x\n",escape);
	} else {
		if (*argv[1] >= '0' && *argv[1] <= '9'){
			escape = htoi(argv[1]);
		} else {
			if (*argv[1] == 'f' || *argv[1] == 'F'){
				if ((escape = atoi(argv[1] + 1) - 12) > -2)
					escape = -2;
			} else {
				escape = uchar(*argv[1]);
			}
		}
	}
	return 0;
}
#ifdef	IP
static
doremote(argc,argv)
int argc;
char *argv[];
{
	struct socket fsock,lsock;
	struct mbuf *bp;

	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;
	} else if(strcmp(argv[3],"exit") == 0){
		*bp->data = SYS_EXIT;
	} else {
		printf("Unknown command %s\n",argv[3]);
		return 1;
	}
	bp->cnt = 1;
	send_udp(&lsock,&fsock,0,0,bp,0,0,0);
	return 0;
}
#endif
#ifdef	MHEARD
/* MHEARD structure, these are linked to the interface struct */
struct mheard {
	struct mheard *next;	/* Doubly linked list pointers */
	struct mheard *prev;

	long time;		/* date/time last heard */
	int  type;		/* type of frame heard */

	union {
#ifdef	AX25
		struct {
			struct ax25 axaddr;
			char to_digi[MAXDIGIS + 1];
		} ax25;
#endif
	} addr;			/* address of heard station */
};
#define NULLMHEARD	(struct mheard *)0

#define MH_AX25		1	/* AX25 addressing */

/*
 * mheard <interface> [<maxheard>|clear]
 */
domheard(argc,argv)
int argc;
char *argv[];

{
	register struct interface *ifp;
	register struct mheard *mhp;

	if((ifp = ifunit(argv[1])) == NULLIF)
		return 1;

	if(argc < 3){
		showmheard(NULLBUF,ifp);
		return 0;
	}

	if(argv[2][0] == 'c'){
		for(mhp = ifp->heard; mhp != NULLMHEARD; mhp = mhp->next)
			free(mhp);

		ifp->heard = ifp->lheard = NULLMHEARD;
		ifp->nheard = 0;
		return 0;
	}

	if((ifp->mheard = atoi(argv[2])) > 100){
		printf("Illegal <maxheard> value %d, set to 0\n",ifp->mheard);
		ifp->mheard = 0;
	}

	while(ifp->nheard > ifp->mheard){
		mhp = ifp->lheard;

		if(mhp->prev == NULLMHEARD)
			ifp->heard = NULLMHEARD;
		else
			mhp->prev->next = NULLMHEARD;

		ifp->lheard = mhp->prev;
		free(mhp);
		ifp->nheard--;
	}
	return 0;
}

showmheard(bpp,ifp)
	struct mbuf **bpp;
	register struct interface *ifp;

{
	register struct mheard *mhp;
	int i,rep;
	char tmp[10],tmp2[10];
	char line[256];
	struct mbuf *qstring();

	for(mhp = ifp->heard; mhp != NULLMHEARD; mhp = mhp->next){
	    sprintf(line,"%.15s  ",ctime(&mhp->time) + 4);
	    switch (mhp->type)
	    {
#ifdef	AX25
	    case MH_AX25:
		pax25(tmp,&mhp->addr.ax25.axaddr.source);
		pax25(tmp2,&mhp->addr.ax25.axaddr.dest);
		sprintf(line + strlen(line),"%-9s to %s",tmp,tmp2);

		if(mhp->addr.ax25.axaddr.ndigis != 0){
		    for(i = strlen(line); i < 40; i++)
			line[i] = ' ';
		    strcpy(line + 40,"via");
		    rep = 0;
		    for(i = 0; i < mhp->addr.ax25.axaddr.ndigis; i++){
			strcat(line," ");
			if(mhp->addr.ax25.to_digi[i])
			    strcat(line,">");
			if(!rep && !(mhp->addr.ax25.axaddr.digis[i].ssid & REPEATED)){
				strcat(line,"(");
				rep++;
			}
			pax25(tmp,&mhp->addr.ax25.axaddr.digis[i]);
			strcat(line,tmp);
		    }
		    if(rep)
			strcat(line,")");
		    line[79] = '\0'; /* truncate extremely long lines */
		}
		break;
#endif
	    }
	    if(bpp != NULLBUFP){
		strcat(line,"\r");
		append(bpp,qstring(line));
	    } else
		printf("%s\n",line);
	}
}

void
dotnmheard (tnc2)
struct tnc *tnc2;

{
#ifdef TNC2
	register struct mheard *mhp;
	int maxlines = 18;			    /* max MH lines for TNC2 */
	char tmp[11],line[60];
	extern char *tnc2_daytime();
	extern void tnc2_mesg();

	for (mhp = tnc2->interface->heard; mhp != NULLMHEARD && maxlines--;
	     mhp = mhp->next) {
		pax25(tmp,&mhp->addr.ax25.axaddr.source);
		if (mhp->addr.ax25.axaddr.ndigis != 0 && !mhp->addr.ax25.to_digi[0])
			strcat(tmp,"*");

		sprintf(line,"%-11s%s\r",tmp,tnc2_daytime(tnc2,mhp->time));
		tnc2_mesg(tnc2,line);
	}
#endif
}
#endif

/* update the MHEARD list for an interface, when a packet is received */
updmheard(ifp,type,addr)
	struct interface *ifp;
	int type;
	register char *addr;

{
#ifdef MHEARD
	register struct mheard *mhp;
	register int i;
	char its_there;
	int addreq();

	/* Update the MHEARD list for this interface */
	if(ifp->mheard != 0){		/* first check if we have to keep it */
	    /* scan existing list to see if already there */
	    for(mhp = ifp->heard; mhp != NULLMHEARD; mhp = mhp->next){
		if(mhp->type == type){	/* same type of addressing? */
		    switch (type)
		    {
#ifdef	AX25
		    case MH_AX25:
			its_there = addreq(&((struct ax25 *)addr)->source,
					   &mhp->addr.ax25.axaddr.source);
			break;
		    default:
			its_there = 0;
			break;
#endif
		    }
		    if(its_there){
			if(mhp->prev != NULLMHEARD)
			    mhp->prev->next = mhp->next;
			else
			    ifp->heard = mhp->next;

			if(mhp->next != NULLMHEARD)
			    mhp->next->prev = mhp->prev;
			else
			    ifp->lheard = mhp->prev;

			break;
		    }
		}
	    }
	    /* if not existing, create a new one */
	    if(mhp == NULLMHEARD){
		if(ifp->nheard >= ifp->mheard){
		    /* reuse oldest if too many entries */
		    mhp = ifp->lheard;
		    mhp->prev->next = NULLMHEARD;
		    ifp->lheard = mhp->prev;
		} else {
		    ifp->nheard++;

		    if((mhp = (struct mheard *) malloc(sizeof(struct mheard))) == NULLMHEARD)
			return;		/* no space, forget about it */
		}

		switch (mhp->type = type)
		{
#ifdef	AX25
		case MH_AX25:
		    for(i = 0; i < MAXDIGIS; i++)
			mhp->addr.ax25.to_digi[i] = 0;
		    break;
#endif
		}
	    }
	    time(&mhp->time);
	    switch (type)
	    {
#ifdef	AX25
	    case MH_AX25:
		for(i = 0; i < ((struct ax25 *)addr)->ndigis; i++){
		    if((((struct ax25 *)addr)->digis[i].ssid & REPEATED) == 0)
			break;
		    if(mhp->addr.ax25.to_digi[i] &&
			!addreq(&mhp->addr.ax25.axaddr.digis[i],
				&((struct ax25 *)addr)->digis[i]))
			    mhp->addr.ax25.to_digi[i] = 0;
		}
		mhp->addr.ax25.to_digi[i] = 1;
		while(++i < MAXDIGIS)
		    mhp->addr.ax25.to_digi[i] = 0;
		ASSIGN(mhp->addr.ax25.axaddr,*((struct ax25 *)addr));
		break;
#endif
	    }
	    /* insert it at head of list */
	    if((mhp->next = ifp->heard) != NULLMHEARD)
		mhp->next->prev = mhp;
	    else
		ifp->lheard = mhp;

	    mhp->prev = NULLMHEARD;
	    ifp->heard = mhp;
	}
#endif
}

