/*
 *  Amiga version changes are 
 *
 *  Copyright (c)1987
 *  Louis A. Mamakos
 *
 *  For non-commercial use only.
 */

/* Main network program - provides both client and server functions */
#define	NSESSIONS	10	/* Maximum interactive client sessions */
#define HOSTNAMELEN 32		/* changed from 16 by Bdale 860812 */

#ifndef	STARTUP
#ifdef	AMIGA
#define	STARTUP "s:net-sequence"
#else
#define	STARTUP	"/autoexec.net"	/* File to read startup commands from */
#endif
#endif

#include <stdio.h>
#include "machdep.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 "session.h"
#include "cmdparse.h"

#ifdef	AMIGA
#include "amiga.h"
#else
#include "pc.h"
#endif

extern struct interface *ifaces;
struct session sessions[NSESSIONS];
struct session *current;
extern char major_rev[], minor_rev[];

int mode;
FILE *logfp;

#ifdef	TRACE
#include "trace.h"
int32 trace;
#endif

char hostname[HOSTNAMELEN];	
int32 aton();
int16 lport = 1001;
#ifdef	AMIGA
char prompt[] = "\33[1;33mnet>\33[m ";	/* make it standout and stuff */
#else
char prompt[] = "net> ";
#endif
char nospace[] = "No space!!\r\n";	/* Generic malloc fail message */
static char notval[] = "Not a valid TCB\r\n";

#ifndef	MSDOS			/* PC uses F-10 key always */
static char escape = 0x1d;	/* default escape character is ^] */
#endif

/* Command lookup and branch table */
int go(),cmdmode(),dodigipeat(),
	doipaddr(),dotelnet(),doexit(),doclose(),dohostname(),
	doreset(),dotcpstat(),dotrace(),doescape(),dospeed(),dohelp(),
	dowindow(),doroute(),doecho(),
	dolog(),doipstat(),doicmpstat(),doetherstat(),memstat(),doarp(),
	dosession(),doftp(),domss(),dostart(),dostop(),doattach(),
	domycall(),dosmtptick(),doudpstat(),dottl(),dokiss(),dotcpkick(),
	doeol();

#ifdef HAPN
int	dohapnstat();
#endif HAPN
#ifdef AMIGADEVDRV
int	DriverInit(), DriverShutdown(), CheckTcp();
#endif
static struct cmds cmds[] = {
	/* The "go" command must be first */
	"",		go,		0, NULLCHAR,	NULLCHAR,
	"arp",		doarp,		0, NULLCHAR,	NULLCHAR,
	"attach",	doattach,	2,
		"attach <hardware> <hw specific options>", NULLCHAR,
	"close",	doclose,	0, NULLCHAR,	NULLCHAR,
#ifdef	AX25
	"digipeat",	dodigipeat,	0, NULLCHAR,	NULLCHAR,
#endif
#ifdef AMIGADEVDRV
	"checktcp",	CheckTcp,	0, NULLCHAR,	NULLCHAR,
	"driver",	DriverInit,	0, NULLCHAR,	NULLCHAR,
	"driveroff",	DriverShutdown,	0, NULLCHAR,	NULLCHAR,
#endif
	"echo",		doecho,		0, NULLCHAR,	NULLCHAR,   
	"eol",		doeol,		0, NULLCHAR,
		"eol options: unix, standard",
#ifndef	MSDOS
	"escape",	doescape,	0, NULLCHAR,	NULLCHAR,   
#endif
#ifdef	PC_EC
	"etherstat",	doetherstat,	0, NULLCHAR,	NULLCHAR,
#endif  PC_EC
	"exit",		doexit,		0, NULLCHAR,	NULLCHAR,
	"ftp",		doftp,		2, "ftp <address>",	NULLCHAR,
#ifdef HAPN
	"hapnstat",	dohapnstat,	0, NULLCHAR,	NULLCHAR,
#endif HAPN
	"help",		dohelp,		0, NULLCHAR,	NULLCHAR,
	"hostname",	dohostname,	0, NULLCHAR,	NULLCHAR,
	"log",		dolog,		0, NULLCHAR,	NULLCHAR,
	"icmpstat",	doicmpstat,	0, NULLCHAR,	NULLCHAR,
	"ipaddr",	doipaddr,	0, NULLCHAR,	NULLCHAR,
	"ipstat",	doipstat,	0, NULLCHAR,	NULLCHAR,
#ifdef	AX25
	"kiss",		dokiss,		0, NULLCHAR,	NULLCHAR,
#endif
	"smtp",		dosmtptick,	0, NULLCHAR,	NULLCHAR,
#ifndef	AMIGA
	"memstat",	memstat,	0, NULLCHAR,	NULLCHAR,
#endif
	"mss",		domss,		0, NULLCHAR,	NULLCHAR,
#ifdef	AX25
	"mycall",	domycall,	0, NULLCHAR,	NULLCHAR,
#endif
	"reset",	doreset,	0, NULLCHAR,	NULLCHAR,
	"route",	doroute,	0, NULLCHAR,	NULLCHAR,
	"session",	dosession,	0, NULLCHAR,	NULLCHAR,
	"speed",	dospeed,	0, NULLCHAR,	NULLCHAR,
#ifdef	SERVERS
	"start",	dostart,	2, "start <servername>",NULLCHAR,
	"stop",		dostop,		2, "stop <servername>",	NULLCHAR,
#endif
	"tcpstat",	dotcpstat,	0, NULLCHAR,	NULLCHAR,
	"tcpkick",	dotcpkick,	0, NULLCHAR,	NULLCHAR,
	"telnet",	dotelnet,	2, "telnet <address>",	NULLCHAR,
	"trace",	dotrace,	0, NULLCHAR,	NULLCHAR,
	"ttl",		dottl,		0, NULLCHAR,	NULLCHAR,
	"udpstat",	doudpstat,	0, NULLCHAR,	NULLCHAR,
	"window",	dowindow,	0, NULLCHAR,	NULLCHAR,
	"?",		dohelp,		0, NULLCHAR,	NULLCHAR,
	NULLCHAR,	NULLFP,		0,
		"Unknown command; type \"?\" for list",   NULLCHAR, 
};

/* "route" subcommands */
int doadd(),dodrop();
static struct cmds rtcmds[] = {
	"add", doadd, 3,
	"route add <dest addr>[/<bits>] <if name> [gateway] [metric]",
	"Add failed",

	"drop", dodrop, 2,
	"route drop <dest addr>[/<bits>]",
	"Not in table",

	NULLCHAR, NULLFP, 0,
	"route subcommands: add, drop",
	NULLCHAR, 
};

#ifdef	SERVERS
/* "start" and "stop" subcommands */
int ftp_start(),smtp_start(),discard_start(),echo_start(),telnet_start();
static struct cmds startcmds[] = {
	"discard",	discard_start,	0, NULLCHAR, NULLCHAR,
	"echo",		echo_start,	0, NULLCHAR, NULLCHAR,
	"ftp",		ftp_start,	0, NULLCHAR, NULLCHAR,
	"smtp",		smtp_start,	0, NULLCHAR, NULLCHAR,
	"telnet",	telnet_start,	0, NULLCHAR, NULLCHAR,
	NULLCHAR,	NULLFP,		0,
		"start options: discard, echo, ftp, smtp, telnet", NULLCHAR,
};
int ftp_stop(),smtp_stop(),echo_stop(),discard_stop(),telnet_stop();
static struct cmds stopcmds[] = {
	"discard",	discard_stop,	0, NULLCHAR, NULLCHAR,
	"echo",		echo_stop,	0, NULLCHAR, NULLCHAR,
	"ftp",		ftp_stop,	0, NULLCHAR, NULLCHAR,
	"smtp",		smtp_stop,	0, NULLCHAR, NULLCHAR,
	"telnet",	telnet_stop,	0, NULLCHAR, NULLCHAR,
	NULLCHAR,	NULLFP,		0,
		"stop options: discard, echo, ftp, smtp, telnet",  NULLCHAR,
};
#endif

main(argc,argv)
int argc;
char *argv[];
{
	static char inbuf[BUFSIZ];	/* keep it off the stack */
	int c;
	char *ttybuf,*fgets();
	int16 cnt;
	int ttydriv();
	int cmdparse();
	void check_time();
	FILE *fp;
	struct interface *ifp;
#ifdef	AMIGA
	char	*startup = STARTUP;
#endif

	ioinit();
	printf("KA9Q Internet Protocol Package, v%s.%s\n",major_rev,minor_rev);
#ifdef	AMIGA
	if (argc > 1)
		startup = argv[1];

	if((fp = fopen(startup,"r")) != NULLFILE){
		while(fgets(inbuf,BUFSIZ,fp) != NULLCHAR){
			printf(prompt); printf(inbuf);
			cmdparse(cmds, inbuf);
		}
		fclose(fp);
	} else
		printf("Can't read inital commands from %s\n", startup);
#else
	if((fp = fopen(STARTUP,"r")) != NULLFILE){
		while(fgets(inbuf,BUFSIZ,fp) != NULLCHAR){
			cmdparse(cmds,inbuf);
		}
		fclose(fp);
	}		
#endif
	cmdmode();
	smtpclinit();			/* arm SMTP background client timer */

	/* Main commutator loop */
	for(;;){
		/* Process any keyboard input */
		while((c = kbread()) != -1){
#ifdef	MSDOS
			/* c == -2 means the command escape key (F10) */
			if(c == -2){
				if(mode != CMD_MODE){
					printf("\r\n");
					cmdmode();
				}
				continue;
			}
#else
			if(c == escape && escape != 0 && mode != CMD_MODE) {
				printf("\r\n");
				cmdmode();
				continue;
			}
#endif
			if((cnt = ttydriv(c,&ttybuf)) == 0)
				continue;
			switch(mode){
			case CMD_MODE:
				(void)cmdparse(cmds,ttybuf);
				fflush(stdout);
				break;
			case CONV_MODE:
#ifndef	MSDOS
				if(ttybuf[0] == escape && escape != 0){
					printf("\r\n");
					cmdmode();
				} else
#endif MSDOS
					if(current->parse != NULLFP)
						(*current->parse)(ttybuf,cnt);

				break;
			}
			if(mode == CMD_MODE){
				printf(prompt);
				fflush(stdout);
			}
		}
		/* Service the interfaces */
		for(ifp = ifaces; ifp != NULLIF; ifp = ifp->next){
			if(ifp->recv != NULLFP)
				(*ifp->recv)(ifp);
		}

		/* Service the clock if it has ticked */
		check_time();
#ifdef AMIGADEVDRV
		/* check device driver open request */
		check_driver();
		CheckTcp();
#else
#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
#endif AMIGADEVDRV
	}
}

/* Enter command mode */
int
cmdmode()
{
	if(mode != CMD_MODE){
		mode = CMD_MODE;
		cooked();
		printf(prompt);
		fflush(stdout);
	}
	return 0;
}
/* Select and display sessions */
static
dosession(argc,argv)
int argc;
char *argv[];
{
	unsigned i;
	struct session *s;
	extern char *tcpstates[];
	char *psocket();

	if(argc < 2){
		printf(" #  Type    Remote socket          TCB  State\r\n");
		for(s=sessions;s < & sessions[NSESSIONS];s++){
			switch(s->type){
			case TELNET:
#ifndef	AMIGA
				printf("%c%-3d%Telnet  %-23s%4x %-s\r\n",
					(current == s)? '*':' ',
					s - sessions,
					psocket(&s->cb.telnet->tcb->conn.remote),
					(int)s->cb.telnet->tcb,
					tcpstates[s->cb.telnet->tcb->state]);
#else
				printf("%c%-3d%Telnet  %-23s%6lx %-s\r\n",
					(current == s)? '*':' ',
					s - sessions,
					psocket(&s->cb.telnet->tcb->conn.remote),
					(unsigned long)s->cb.telnet->tcb,
					tcpstates[s->cb.telnet->tcb->state]);
#endif				break;
			case FTP:
#ifndef	AMIGA
				printf("%c%-3d%FTP     %-23s%4x %-s\r\n",
					(current == s)? '*':' ',
					s - sessions,
					psocket(&s->cb.ftp->control->conn.remote),
					(int)s->cb.ftp->control,
					tcpstates[s->cb.ftp->control->state]);
#else
				printf("%c%-3d%FTP     %-23s%6lx %-s\r\n",
					(current == s)? '*':' ',
					s - sessions,
					psocket(&s->cb.ftp->control->conn.remote),
					(unsigned long)s->cb.ftp->control,
					tcpstates[s->cb.ftp->control->state]);
#endif
				break;
			}
		}
		return 0;
	}
	i = atoi(argv[1]);
	if(i > NSESSIONS){
		printf("Invalid session: %d\r\n",i);
		return 1;
	}
	if(sessions[i].type == FREE){
		printf("Inactive session: %d\r\n",i);
		return 1;
	}
	current = &sessions[i];
	go();
	return 0;
}
/* Enter conversational mode with current session */
int
go()
{
	void rcv_char(),r_ctl();

	if(current == NULLSESSION || current->type == FREE)
		return 0;
	mode = CONV_MODE;
	switch(current->type){
	case TELNET:
		if(current->cb.telnet->remote[TN_ECHO])
			raw();	/* Re-establish raw mode if it was set */
		rcv_char(current->cb.telnet->tcb,0); /* Get any pending input */
		break;
	case FTP:
		r_ctl(current->cb.ftp->control,0);
		break;
	}
	return 0;
}
#ifdef	AX25
extern int digipeat;
dodigipeat(argc,argv)
int argc;
char *argv[];
{
	if(argc == 1) {
		printf("digipeat %s\r\n",digipeat ? "on" : "off");
	} else {
		if(strcmp(argv[1],"on") == 0)
			digipeat = 1;
		else
			digipeat = 0;
	}
}
#endif
static
doipaddr(argc,argv)
int argc;
char *argv[];
{
	char *inet_ntoa();

	if(argc < 2)
		printf("%s\r\n",inet_ntoa(ip_addr));
	else
		ip_addr = aton(argv[1]);
	return 0;
}
static
doexit(argc,argv)
int argc;
char *argv[];
{
	iostop();
	DriverShutdown();
	exit(0);
}
static
doclose(argc,argv)
int argc;
char *argv[];
{
	if(current == NULLSESSION){
		printf("No current session\r\n");
		return 0;
	}
	switch(current->type){
	case TELNET:
		close_tcp(current->cb.telnet->tcb);
		break;
	case FTP:
		close_tcp(current->cb.ftp->control);
		break;
	}
	return 0;
}
static
doreset(argc,argv)
int argc;
char *argv[];
{
	long htol();
	struct tcb *tcb;

	if(argc < 2){
		if(current == NULLSESSION){
			printf("No current session\r\n");
			return 1;
		}
		switch(current->type){
		case TELNET:
			tcb = current->cb.telnet->tcb;
			break;
		case FTP:
			tcb = current->cb.ftp->control;
			break;
		}
	} else
		tcb = (struct tcb *)htol(argv[1]);
	if(!tcpval(tcb)){
		printf(notval);
		return 1;
	}
	close_self(tcb,RESET);
	return 0;
}
static
int
dotcpstat(argc,argv)
int argc;
char *argv[];
{
	long htol();
	register struct tcb *tcb;

	if(argc < 2){
		tcpstat();
	} else {
		tcb = (struct tcb *)htol(argv[1]);
		if(tcpval(tcb))
			state_tcp(tcb);
		else
			printf(notval);
	}
	return 0;
}
static
int
dotcpkick(argc,argv)
int argc;
char *argv[];
{
	long htol();
	register struct tcb *tcb;
	void tcp_timeout();

	if(argc < 2){
		if(current == NULLSESSION){
			printf("No current session\r\n");
			return 1;
		}
		switch(current->type){
		case TELNET:
			tcb = current->cb.telnet->tcb;
			break;
		case FTP:
			tcb = current->cb.ftp->control;
			break;
		}
	} else
		tcb = (struct tcb *)htol(argv[1]);

	if(!tcpval(tcb)){
		printf(notval);
		return 1;
	}
	if(argc > 2){
		/* Optional value for SRTT */
		tcb->srtt = atoi(argv[2]);
	}
	/* Don't actually do anything unless something is pending */
	if(tcb->sndcnt != 0){
		tcb->retry = 0;
		tcp_timeout((int *)tcb);
	}
	return 0;
}
static
int
dotrace(argc,argv)
int argc;
char *argv[];
{
	long htol();
#ifdef TRACE
	if(argc < 2)
		printf("trace level 0x%08lx\r\n",trace);
	else
		trace = htol(argv[1]);
	return 0;
#else
	printf("Trace not enabled - recompile with TRACE defined\r\n");
#endif
}
static
dohostname(argc,argv)
int argc;
char *argv[];
{
	char *strncpy();

	if(argc < 2)
		printf("%s\r\n",hostname);
	else 
		strncpy(hostname,argv[1],HOSTNAMELEN);
	return 0;
}
static
int
dolog(argc,argv)
int argc;
char *argv[];
{
	char *strncpy();

#ifdef	AMIGA
	static char logname[45];  /* long enough for CON:10/10/300/300/title */
#else
	static char logname[15];
#endif
	if(argc < 2){
		if(logfp)
			printf("Logging to %s\r\n",logname);
		else
			printf("Logging off\r\n");
		return 0;
	}
	if(logfp){
		fclose(logfp);
		logfp = NULLFILE;
	}
	if(strcmp(argv[1],"stop") != 0){
#ifdef	AMIGA
		strncpy(logname,argv[1],45);
#else
		strncpy(logname,argv[1],15);
#endif
		logfp = fopen(logname,"a+");
	}
	return 0;
}
#ifndef	MSDOS
static
int
doescape(argc,argv)
int argc;
char *argv[];
{
	if(argc < 2)
		printf("0x%x\r\n",escape);
	else 
		escape = *argv[1];
	return 0;
}
#endif	MSDOS
static
int
dohelp(argc,argv)
int argc;
char *argv[];
{
	register struct cmds *cmdp;
	int i,j;

	printf("Main commands:\r\n");
	for(i=0,cmdp = cmds;cmdp->name != NULL;cmdp++,i++){
		printf("%s",cmdp->name);
		if((i % 4) == 3)
			printf("\r\n");
		else {
			for(j=strlen(cmdp->name);j < 16; j++)
				putchar(' ');
		}
	}
	if((i % 4) != 0)
		printf("\r\n");
	return 0;
}
static
int
domss(argc,argv)
int argc;
char *argv[];
{
	if(argc < 2)
		printf("%d\r\n",tcp_mss);
	else
		tcp_mss = atoi(argv[1]);
	return 0;
}
static
int
dowindow(argc,argv)
int argc;
char *argv[];
{
	if(argc < 2)
		printf("%d\r\n",tcp_window);
	else
		tcp_window = atoi(argv[1]);
	return 0;
}
static
int
dottl(argc,argv)
char *argv[];
{
	if(argc < 2)
		printf("%u\r\n",ip_ttl & 0xff);
	else
		ip_ttl = atoi(argv[1]);
	return 0;
}
struct session *
newsession()
{
	register int i;

	for(i=0;i<NSESSIONS;i++)
		if(sessions[i].type == FREE)
			return &sessions[i];
	return NULLSESSION;
}
freesession(s)
struct session *s;
{
	if(s != NULLSESSION)
		s->type = FREE;
}
/* Display and/or manipulate routing table */
int
doroute(argc,argv)
int argc;
char *argv[];
{
	if(argc < 2){
		dumproute();
		return 0;
	}
	return subcmd(rtcmds,argc,argv);
}
/* Add an entry to the routing table
 * E.g., "add 1.2.3.4 ax0 5.6.7.8 3"
 */
int
doadd(argc,argv)
int argc;
char *argv[];
{
	struct interface *ifp;
	int32 dest,gateway;
	unsigned bits;
	char *bitp,*index();
	int metric;

	if(strcmp(argv[1],"default") == 0){
		dest = 0;
		bits = 0;
	} else {
		dest = aton(argv[1]);

		/* If IP address is followed by an optional slash and
		 * a length field, (e.g., 128.96/16) get it;
		 * otherwise assume a full 32-bit address
		 */
		if((bitp = index(argv[1],'/')) != NULLCHAR){
			bitp++;
			bits = atoi(bitp);
		} else
			bits = 32;
	}
	for(ifp=ifaces;ifp != NULLIF;ifp = ifp->next){
		if(strcmp(argv[2],ifp->name) == 0)
			break;
	}
	if(ifp == NULL){
		printf("Interface \"%s\" unknown\r\n",argv[2]);
		return 1;
	}
	if(argc > 3)
		gateway = aton(argv[3]);
	else
		gateway = 0;

	if(argc > 4)
		metric = atoi(argv[4]);
	else
		metric = 0;

	rt_add(dest,bits,gateway,metric,ifp);
	return 0;
}
/* Drop an entry from the routing table
 * E.g., "drop 128.96/16
 */
int
dodrop(argc,argv)
int argc;
char *argv[];
{
	char *bitp,*index();
	unsigned bits;

	/* If IP address is followed by an optional slash and length field,
	 * (e.g., 128.96/16) get it; otherwise assume a full 32-bit address
	 */
	if((bitp = index(argv[1],'/')) != NULLCHAR){
		bitp++;
		bits = atoi(bitp);
	} else
		bits = 32;

	return rt_drop(aton(argv[1]),bits);
}

#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 SERVERS

doecho(argc,argv)
int argc;
char *argv[];
{
	extern int refuse_echo;

	if(argc < 2){
		if(refuse_echo)
			printf("Refuse\r\n");
		else
			printf("Accept\r\n");
	} else {
		if(strcmp(argv[1],"refuse") == 0)
			refuse_echo = 1;
		else if(strcmp(argv[1],"accept") == 0)
			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\r\n");
		else
			printf("Standard\r\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;
}
/* List of supported hardware devices */
#ifdef	PC_EC
int ec_attach();
#endif

int asy_attach();

#ifdef	PC100
int pc_attach();
#endif

#ifdef HAPN
int hapn_attach();
#endif

#ifdef	NETROM
int netrom_attach();
#endif

struct cmds attab[] = {
#ifdef	NETROM
	/* NET/ROM virtual interface using companion asy interface in AX.25 mode */
	"netrom", netrom_attach, 4,
	"attach netrom <asy-intf> ax25 <label> [mtu [ upd-freq ]]",
	"Could not attach NET/ROM virtual interface",
#endif
#ifdef	PC_EC
	/* 3-Com Ethernet interface */
	"3c500", ec_attach, 7, 
	"attach 3c500 <address> <vector> arpa <label> <buffers> <mtu>",
	"Could not attach 3c500",
#endif
	/* Ordinary PC asynchronous adaptor */
	"asy", asy_attach, 8, 
	"attach asy <address> <vector> slip|ax25 <label> <buffers> <mtu> <speed>",
	"Could not attach asy",
#ifdef	PC100
	/* PACCOMM PC-100 8530 HDLC adaptor */
	"pc100", pc_attach, 8, 
	"attach pc100 <address> <vector> ax25 <label> <buffers> <mtu> <speed>",
	"Could not attach pc100",
#endif
#ifdef	HAPN
	/* Hamilton Area Packet Radio (HAPN) 8273 HDLC adaptor */
	"hapn", hapn_attach, 8,
	"attach hapn <address> <vector> ax25 <label> <rx bufsize> <mtu> csma|full",
	"Could not attach hapn",
#endif
	NULLCHAR, NULLFP, 0,
	"Unknown device",
	NULLCHAR,
};

/* Attach an interface
 * Syntax: attach <hw type> <I/O address> <vector> <mode> <label> <bufsize> [<speed>]
 */
doattach(argc,argv)
int argc;
char *argv[];
{
	return subcmd(attab,argc,argv);
}
int
dospeed(argc,argv)
int argc;
char *argv[];
{
	int i;

	if(argc < 3){
		for(i=0;i < nasy; i++){
			printf("%d: %d bps\r\n",i,asy[i].speed);
		}
		return 0;
	}
	i = atoi(argv[1]);
	if(i >= nasy){
		printf("Line %d out of range\r\n",i);
		return 0;
	}
	asy_speed(i,atoi(argv[2]));
	return i;
}

/* Log messages of the form
 * Tue Jan 31 00:00:00 1987 44.64.0.7:1003 open FTP
 */
/*VARARGS2*/
log(tcb,fmt,arg1,arg2,arg3,arg4)
struct tcb *tcb;
char *fmt;
int arg1,arg2,arg3,arg4;
{
	char *cp,*cp1,*ctime(),*index();
	long t;

	if(logfp == NULLFILE)
		return;
	time(&t);
	cp = ctime(&t);
	if((cp1 = index(cp,'\n')) != NULLCHAR)
		*cp1 = '\0';
	fprintf(logfp,"%s %s - ",cp,psocket(&tcb->conn.remote));
	fprintf(logfp,fmt,arg1,arg2,arg3,arg4);
	fprintf(logfp,"\n");
	fflush(logfp);
}
/* Define null definitions for unused interfaces to avoid pulling in code
 * from the library
 */
#ifndef	ETHER
int ec_output() {}
int pether() {}
int gether() {}
char *ether_bdcst = NULLCHAR;
#endif ETHER

#ifndef AX25
int setcall() {}
int psax25() {}
struct ax25_addr ax25_bdcst,mycall;
#endif AX25
