#include <stdio.h>
#include "global.h"
#include "mbuf.h"
#include "ax25.h"
#include "timer.h"
#include "iface.h"
#include "lapb.h"
#include "cmdparse.h"
#include "session.h"

char *ax25states[] = {
	"Disconnected",
	"Conn pending",
	"Disc pending",
	"Connected",
	"Frame Reject"
};

char *ax25mesgs[] = {
	"",
	"CONNECTED to",
	"DISCONNECTED from",
	"BUSY from",
	"LINK FAILURE with",
	"LINK RESET from",
	"LINK RESET to",
	"FRAME REJECT from",
	"FRAME REJECT to",
};

char badcall[] = "Bad callsign %s\n";
char baddigi[] = "Bad digi callsign %s\n";
char notax25[] = "Interface %s not AX.25 type\n";

static char all_ax[] = "ALL AX.25";
static char conn[] = "conn";
static char digi[] = "digi";
static char gate[] = "gate";
static char on[] = "on";
static char off[] = "off";
static char stop[] = "stop";

extern char nospace[];

static int doaxport(),dodigipeat(),doaxstat(),dot1(),dot2(),dot3(),dot4(),
	doexclude(),dopthresh(),domaxframe(),doaxwindow(),dopaclen(),don2(),
	doaxclose(),doaxdisc(),doaxreset(),doaxkick(),doaxbreak(),domaxsamml();
int dopersist();

int doaxstart(),doaxstop();

static struct cmds axcmds[] = {
#ifdef AX25BREAK
	"break",	doaxbreak,	4, "<iface> <call1> <call2> [<digi>]", NULLCHAR,
#endif
	"close",	doaxclose,	2, "<axcb>", NULLCHAR,
	"digipeat",	dodigipeat,	0, NULLCHAR,	NULLCHAR,
	"disconnect",	doaxdisc,	2, "<axcb>", NULLCHAR,
	"exclude",	doexclude,	0, NULLCHAR,	NULLCHAR,
	"kick",		doaxkick,	2, "<axcb>", NULLCHAR,
	"maxframe",	domaxframe,	0, NULLCHAR,	NULLCHAR,
	"maxsammler",	domaxsamml,	0, NULLCHAR,	NULLCHAR,
	"paclen",	dopaclen,	0, NULLCHAR,	NULLCHAR,
	"persist",	dopersist,	0, NULLCHAR,	NULLCHAR,
	"port",		doaxport,	0, NULLCHAR,	"Couldn't set port",
	"pthresh",	dopthresh,	0, NULLCHAR,	NULLCHAR,
	"reset",	doaxreset,	2, "<axcb>", NULLCHAR,
	"retry",	don2,		0, NULLCHAR,	NULLCHAR,
	"status",	doaxstat,	0, NULLCHAR,	NULLCHAR,
	"start",	doaxstart,	2, "<servername>",NULLCHAR,
	"stop",		doaxstop,	2, "<servername>", NULLCHAR,
	"t1",		dot1,		0, NULLCHAR,	NULLCHAR,
	"t2",		dot2,		0, NULLCHAR,	NULLCHAR,
	"t3",		dot3,		0, NULLCHAR,	NULLCHAR,
	"t4",		dot4,		0, NULLCHAR,	NULLCHAR,
	"window",	doaxwindow,	0, NULLCHAR,	NULLCHAR,
	NULLCHAR,	NULLFP,		0, "?subcommands", NULLCHAR
};
/* Multiplexer for top-level ax25 command */
doax25(argc,argv)
int argc;
char *argv[];
{
	return subcmd(axcmds,argc,argv);
}

/* Reset an AX.25 session */
static
doaxreset(argc,argv)
int argc;
char *argv[];
{
	struct ax25_cb *axp;
	extern char notval[];

	axp = (struct ax25_cb *)long2ptr(htol(argv[1]));
	if(!ax25val(axp)){
		printf(notval);
		return 1;
	}
	reset_ax25(axp);
	return 0;
}

/* Close an AX.25 session (= graceful disconnect) */
static
doaxclose(argc,argv)
int argc;
char *argv[];
{
	struct ax25_cb *axp;
	void close_ax25();
	extern char notval[];

	axp = (struct ax25_cb *)long2ptr(htol(argv[1]));
	if(!ax25val(axp)){
		printf(notval);
		return 1;
	}
	close_ax25(axp);
	return 0;
}

/* Disconnect an AX.25 session */
static
doaxdisc(argc,argv)
int argc;
char *argv[];
{
	struct ax25_cb *axp;
	void disc_ax25();
	extern char notval[];

	axp = (struct ax25_cb *)long2ptr(htol(argv[1]));
	if(!ax25val(axp)){
		printf(notval);
		return 1;
	}
	disc_ax25(axp);
	return 0;
}

#ifdef AX25BREAK
/* Breakdown an AX.25 connection */
/* This really anti-social function was only implemented to stop BBS */
/* systems from forwarding huge amounts of mail using AX.25 when the */
/* channel already is very busy.  Probably can be removed soon (??)  */

static int
doaxbreak(argc,argv)
int argc;
char *argv[];
{
	struct ax25_cb axcb;
	extern char nospace[];

	memset(&axcb,0,sizeof(struct ax25_cb));

	if((axcb.interface = ifunit(argv[1])) == NULLIF)
		return 1;
	if(!(axcb.interface->flags & IF_AX25)){
		printf(notax25,argv[1]);
		return 1;
	}
	if(setcall(&axcb.addr.source,argv[2]) < 0){
		printf(badcall,argv[2]);
		return 1;
	}
	if(setcall(&axcb.addr.dest,argv[3]) < 0){
		printf(badcall,argv[3]);
		return 1;
	}
	axcb.addr.ndigis = 0;
	if(argc > 4){
		if(setcall(&axcb.addr.digis[0],argv[4]) < 0){
			printf(baddigi,argv[4]);
			return 1;
		}
		axcb.addr.ndigis = 1;
	}
	sendctl(&axcb,COMMAND,DISC|PF);
	sendctl(&axcb,RESPONSE,DM|PF);
	setcall(&axcb.addr.source,argv[3]);
	setcall(&axcb.addr.dest,argv[2]);
	sendctl(&axcb,COMMAND,DISC|PF);
	sendctl(&axcb,RESPONSE,DM|PF);
	stop_timer(&axcb.t1);
	return 0;
}
#endif

/* Display AX.25 link level control blocks */
static
doaxstat(argc,argv)
int argc;
char *argv[];
{
	register int i;
	register struct ax25_cb *axp;
	char tmps[10],tmpd[10];
	extern char notval[];

	if(argc < 2){
		printf("    &AXB IF    Rcv-Q   Snd-Q   Local     Remote    State\n");
		for(i=0;i<NHASH;i++){
			for(axp = ax25_cb[i];axp != NULLAX25; axp = axp->next){
				pax25(tmps,&axp->addr.source);
				pax25(tmpd,&axp->addr.dest);
				printf("%8lx %-6s%5d%8d   %-10s%-10s%s\n",
					ptr2long(axp),axp->interface->name,
					len_mbuf(axp->rxq),len_q(axp->txq),
					tmps,tmpd,ax25states[axp->state]);
			}
		}
		return 0;
	}
	axp = (struct ax25_cb *)long2ptr(htol(argv[1]));
	if(!ax25val(axp)){
		printf(notval);
		return 1;
	}
	dumpstat(axp);
	return 0;
}
/* Dump one control block */
static
dumpstat(axp)
register struct ax25_cb *axp;
{
	char tmps[10],tmp[10];
	int i;

	if(axp == NULLAX25 || axp->interface == NULLIF)
		return;
	printf("    &AXB IF   Local     Remote    RBWF V(S) V(R) Unack P Retry State\n");
	pax25(tmps,&axp->addr.source);
	pax25(tmp,&axp->addr.dest);
	printf("%8lx %-5s%-10s%-10s",ptr2long(axp),axp->interface->name,tmps,tmp);
	putchar(axp->rejsent ? 'R' : ' ');
	putchar(axp->remotebusy ? 'B' : ' ');
	putchar(axp->waitack ? 'W' : ' ');
	putchar(axp->fin ? 'F' : ' ');
	printf(" %4d %4d",axp->vs,axp->vr);
	printf(" %u/%u/%u %u",axp->unack,axp->sent,axp->maxframe,axp->proto);
	printf(" %02u/%02u",axp->retries,axp->n2);
	printf(" %s\n",ax25states[axp->state]);

	printf("T1: ");
	if(run_timer(&axp->t1))
		printf("%lu",TICK2MS(axp->t1.start - axp->t1.count));
	else
		printf(stop);
	printf("/%lu ms; ",TICK2MS(axp->t1.start));

	printf("T2: ");
	if(run_timer(&axp->t2))
		printf("%lu",TICK2MS(axp->t2.start - axp->t2.count));
	else
		printf(stop);
	printf("/%lu ms; ",TICK2MS(axp->t2.start));

	printf("T3: ");
	if(run_timer(&axp->t3))
		printf("%lu",TICK2MS(axp->t3.start - axp->t3.count));
	else
		printf(stop);
	printf("/%lu ms; ",TICK2MS(axp->t3.start));

	printf("T4: ");
	if(run_timer(&axp->t4))
		printf("%lu",TICK2MS(axp->t4.start - axp->t4.count));
	else
		printf(stop);
	printf("/%lu ms",TICK2MS(axp->t4.start));

	for(i = 0; i < 8; i++)
		if(axp->sammlq[i] != NULLBUF){
			printf("\nFramesammler: %d",i++);
			for(; i < 8; i++)
				if(axp->sammlq[i] != NULLBUF)
					printf(" %d",i);
			printf(" (max %d)",axp->maxsamml);
		}

	if(axp->addr.ndigis != 0){
		printf("\nDigipeaters:");
		for(i=0;i<axp->addr.ndigis;i++){
			pax25(tmp,&axp->addr.digis[i]);
			printf(" %s",tmp);
		}
	}
	printf("\n");
}

/* Display or change our AX.25 ports
 * usage:
 * ax25 port <portnumber> conn|digi <callsign> [<interface> [gate|multi]]
 * ax25 port
 */
static
doaxport(argc,argv)
int argc;
char *argv[];
{
	register struct ax25_call *axc;
	struct ax25_call **which;
	struct interface *ifp = NULLIF;
	struct ax25_addr mycall;
	char tmp[10];
	int port;
	void ax_noserv();

	if(argc < 2){
		printf("port#  Callsign   Interface  Mode  MG  Started\n");
		if((axc = ax25_port) != NULLAXCALL)
			while(axc->p_next != NULLAXCALL)
				axc = axc->p_next;

		while(axc != NULLAXCALL){
			pax25(tmp,&axc->addr);
			printf("%4d   %-10s %-10s %-6s%c%c  ",axc->port,tmp,
				  (axc->interface == NULLIF?
					all_ax:axc->interface->name),
				  (axc->mode == CONNECT? conn:digi),
				  (axc->flags & MULTI_IF? 'M':' '),
				  (axc->flags & DIGIGATEWAY? 'G':' '));

			if(axc->s_upcall == ax_noserv)
				printf("no\n");
			else
				printf("yes\n");

			axc = axc->p_prev;
		}
		return 0;
	}

	if(argc < 4){
		printf("Usage: ax25 port <number> conn|digi <callsign> [<interface> [gate|multi]]\n");
		return 1;
	}

	if((port = atoi(argv[1])) <= 0)
		return -1;

	if(argv[2][0] == 'd')
		which = ax25_digi;
	else
		which = ax25_call;

	if(setcall(&mycall,argv[3]) == -1)
		return -1;

	if(argc > 4 && (ifp = ifunit(argv[4])) == NULLIF)
		return -1;

	if((axc = cr_axcall(which,&mycall)) == NULLAXCALL)
		return -1;

	if(axc->port == 0){		/* not already existing? */
		/* Insert at beginning of port chain */
		axc->p_prev = NULLAXCALL;
		axc->p_next = ax25_port;
		if(axc->p_next != NULLAXCALL)
			axc->p_next->p_prev = axc;
		ax25_port = axc;
		axc->s_upcall = ax_noserv; /* set default to "no server" */
	}

	axc->port = port;
	axc->flags = 0;
	if(ifp == NULLIF ||
	   (axc->interface != NULLIF && axc->interface != ifp))
		axc->flags |= MULTI_IF;
	else
		axc->interface = ifp;

	if(argc > 5)
		switch (argv[5][0])
		{
		case 'g':
			axc->flags |= DIGIGATEWAY;
			break;
		case 'm':
			axc->flags |= MULTI_IF;
			break;
		}

	if(argv[2][0] == 'd')
		axc->mode = DIGICONNECT;
	else
		axc->mode = CONNECT;

	return 0;
}

/* Display or change AX.25 excluded callsigns
 * usage:
 * ax25 exclude <callsign> [<interface>|d]
 * ax25 exclude
 */
static
doexclude(argc,argv)
int argc;
char *argv[];
{
	register struct ax25_call *axc;
	struct interface *ifp = NULLIF;
	struct ax25_addr call;
	int i;
	char tmp[10];

	if(argc < 2){
	    printf("Callsign   Interface\n");
	    for (i = 0; i < NHASH; i++){
		for (axc = ax25_excl[i]; axc != NULLAXCALL; axc = axc->next){
		    pax25(tmp,&axc->addr);
		    printf("%-10s %s\n",tmp,(axc->interface == NULLIF?
				    all_ax:axc->interface->name));
		}
	    }
	    return 0;
	}

	if(setcall(&call,argv[1]) == -1){
		printf("Usage: ax25 exclude <callsign> [<interface>|d]\n");
		return -1;
	}

	call.ssid = 0;			/* always use the -0 SSID */

	if(argc > 2){
		if(!strcmp(argv[2],"d")){
			if((axc = find_axcall(ax25_excl,&call)) != NULLAXCALL){
				del_axcall(ax25_excl,axc);
				return 0;
			} else {
				pax25(tmp,&call);
				printf("%s not in exclude list\n",tmp);
				return -1;
			}
		}

		if((ifp = ifunit(argv[2])) == NULLIF)
			return -1;
	}

	if((axc = cr_axcall(ax25_excl,&call)) == NULLAXCALL)
		return -1;

	axc->flags = 0;
	if(ifp == NULLIF ||
	   (axc->interface != NULLIF && axc->interface != ifp)){
		axc->interface = NULLIF;
		axc->flags |= MULTI_IF;
	} else
		axc->interface = ifp;

	return 0;
}

/* Control AX.25 digipeating
 * usage:
 * ax25 digipeat off|on
 * ax25 digipeat <interface> [off|on|gate]
 */
static
dodigipeat(argc,argv)
int argc;
char *argv[];
{
	extern int digipeat;
	struct interface *ifp;
	register struct ax25_call *axd;

	switch (argc)
	{
	case 1:
	    printf("digipeat %s\n",digipeat ? on : off);
	    break;

	case 2:
	    if(strcmp(argv[1],on) == 0)
		digipeat = 1;
	    else
		if(strcmp(argv[1],off) == 0)
		    digipeat = 0;
		else
		    if((ifp = ifunit(argv[1])) != NULLIF){
			printf("digipeat %s ",argv[1]);

		    if((axd = find_axcall(ax25_digi,ifp->hwaddr)) != NULLAXCALL)
			printf("%s\n",axd->flags & DIGIGATEWAY?
				      gate:on);
		    else printf("off\n");
		    }
	    break;

	case 3:
	    if((ifp = ifunit(argv[1])) != NULLIF){
		if(strcmp(argv[2],off) == 0){
		    if((axd = find_axcall(ax25_digi,ifp->hwaddr)) != NULLAXCALL){
			if(axd->interface == ifp)
			    del_axcall(ax25_digi,axd);
		    }
		} else {
		    if((axd = cr_axcall(ax25_digi,ifp->hwaddr)) != NULLAXCALL){
			axd->mode = DIGIPEAT;
			axd->flags = 0;
			if(axd->interface == NULLIF || axd->interface == ifp){
			    if(strcmp(argv[2],gate) == 0)
				    axd->flags |= DIGIGATEWAY;
			    axd->interface = ifp;
			}
		    }
		}
	    }
	    break;
	}
	return 0;
}

/* Set retransmission timer */
static
dot1(argc,argv)
int argc;
char *argv[];
{
	extern int16 t1init;

	if(argc == 1) {
		printf("T1 %lu ms\n",TICK2MS(t1init));
	} else {
		t1init = MS2TICK(atol(argv[1]));
	}
	return 0;
}

/* Set acknowledgement delay timer */
static
dot2(argc,argv)
int argc;
char *argv[];
{
	extern int16 t2init;

	if(argc == 1) {
		printf("T2 %lu ms\n",TICK2MS(t2init));
	} else {
		t2init = MS2TICK(atol(argv[1]));
	}
	return 0;
}

/* Set idle timer */
static
dot3(argc,argv)
int argc;
char *argv[];
{
	extern int16 t3init;

	if(argc == 1) {
		printf("T3 %lu ms\n",TICK2MS(t3init));
	} else {
		t3init = MS2TICK(atol(argv[1]));
	}
	return 0;
}

/* Set link idle disconnect timer */
static
dot4(argc,argv)
int argc;
char *argv[];
{
	extern int16 t4init;

	if(argc == 1) {
		printf("T4 %lu ms\n",TICK2MS(t4init));
	} else {
		t4init = MS2TICK(atol(argv[1]));
	}
	return 0;
}

/* Set retry limit count */
static
don2(argc,argv)
int argc;
char *argv[];
{
	extern int16 n2;

	if(argc == 1) {
		printf("Retry %u\n",n2);
	} else {
		n2 = atoi(argv[1]);
	}
	return 0;
}

/* Set maximum number of frames that will be allowed in flight */
static
domaxframe(argc,argv)
int argc;
char *argv[];
{
	extern int16 maxframe;

	if(argc == 1) {
		printf("Maxframe %u\n",maxframe);
	} else {
		maxframe = atoi(argv[1]) & MMASK;
	}
	return 0;
}

/* Set maximum number of frames that will be accepted by "framesammler" */
static
domaxsamml(argc,argv)
int argc;
char *argv[];
{
	extern int16 maxsamml;

	if(argc == 1) {
		printf("Maxsammler %u\n",maxsamml);
	} else {
		maxsamml = atoi(argv[1]) & MMASK;
	}
	return 0;
}

/* Set maximum length of I-frame data field */
static
dopaclen(argc,argv)
int argc;
char *argv[];
{
	extern int16 paclen;

	if(argc == 1) {
		printf("Paclen %u\n",paclen);
	} else {
		paclen = atoi(argv[1]);
	}
	return 0;
}

/* Set size of I-frame above which polls will be sent after a timeout */
static
dopthresh(argc,argv)
int argc;
char *argv[];
{
	extern int16 pthresh;

	if(argc == 1) {
		printf("Pthresh %u\n",pthresh);
	} else {
		pthresh = atoi(argv[1]);
	}
	return 0;
}

/* Set high water mark on receive queue that triggers RNR */
static
doaxwindow(argc,argv)
int argc;
char *argv[];
{
	extern int16 axwindow;

	if(argc == 1) {
		printf("Axwindow %u\n",axwindow);
	} else {
		axwindow = atoi(argv[1]);
	}
	return 0;
}

/* Force a retransmission */
static int
doaxkick(argc,argv)
int argc;
char *argv[];
{
	register struct ax25_cb *axp;
	extern char notval[];

	axp = (struct ax25_cb *)long2ptr(htol(argv[1]));
	if(kick_ax25(axp) == -1){
		printf(notval);
		return 1;
	}
	return 0;
}

/* End of ax25 subcommands */

/* Initiate interactive AX.25 connect to remote station */
doconnect(argc,argv)
int argc;
char *argv[];
{
	void ax_rx(),ax_tx(),ax_state();
	int ax_parse();
	struct ax25 addr;
	struct ax25_cb *open_ax25();
	register struct ax25_call *axc;
	struct interface *ifp;
	register struct session *s;
	extern int16 axwindow;
	int i;

	if((ifp = ifunit(argv[1])) == NULLIF)
		return 1;
	if(!(ifp->flags & IF_AX25)){
		printf(notax25,argv[1]);
		return 1;
	}
	memset(&addr,0,sizeof(addr));
	if(setcall(&addr.dest,argv[2]) < 0){
		printf(badcall,argv[2]);
		return 1;
	}
	/* As default, use the callsign of the interface */
	ASSIGN(addr.source,*((struct ax25_addr *)ifp->hwaddr));
	/* Then, see if we can use a TNC call on this interface */
	for (axc = ax25_port; axc != NULLAXCALL; axc = axc->p_next){
		if(axc->port == TNCPORT &&
		   ((axc->flags & MULTI_IF) || axc->interface == ifp)){
		    ASSIGN(addr.source,axc->addr);
		    break;
		}
	}
	/* Set digipeater path */
	if((addr.ndigis = argc - 3) > MAXDIGIS){
		printf("Too many digipeaters\n");
		return 1;
	}
	for(i=3; i < argc; i++)
		if(setcall(&addr.digis[i-3],argv[i]) < 0){
			printf(baddigi,argv[i]);
			return 1;
		}
	/* See if a session already exists */
	for(s = sessions; s < &sessions[nsessions]; s++){
		if(s->type == AX25TNC
		 && addreq(&s->cb.ax25_cb->addr.dest,&addr.dest)
		 && addreq(&s->cb.ax25_cb->addr.source,&addr.source)){
			printf("Session %lu to %s already exists\n",
				(long) (s - sessions),argv[2]);
			return 1;
		}
	}
	/* Allocate a session descriptor */
	if((s = newsession()) == NULLSESSION){
		printf("Too many sessions\n");
		return 1;
	}
	if((s->name = malloc((unsigned)strlen(argv[2])+1)) != NULLCHAR)
		strcpy(s->name,argv[2]);
	s->type = AX25TNC;
	s->parse = ax_parse;
	current = s;

	if ((s->cb.ax25_cb = open_ax25(&addr,axwindow,ax_rx,ax_tx,ax_state,
				       ifp,(char *)s)) == NULLAX25){
		freesession(s);
		printf("Can't connect to %s\n",argv[2]);
		return 1;
	}

	s->cb.ax25_cb->t4.start = 0;	/* disable idle disconnect */

	go();
	return 0;
}


/* Display changes in AX.25 state */
void
ax_state(axp,old,new,msg)
struct ax25_cb *axp;
int old,new,msg;
{
	struct session *s;
	char tmp[100];

	s = (struct session *)axp->user;

	if(current != NULLSESSION && current->type == AX25TNC && current == s){
	    if (msg && msg <= (sizeof(ax25mesgs)/sizeof(char *))){
		pdax25(tmp,&axp->addr);
		printf("*** %s %s\n",ax25mesgs[msg],tmp);
#if 0
	    } else {
		printf("%s\n",ax25states[new]);
#endif
	    }
	    if(new == DISCONNECTED)
		cmdmode();
	    fflush(stdout);
	}
	if(new == DISCONNECTED){
	    axp->user = NULLCHAR;
	    freesession(s);
	}
}
/* Handle typed characters on AX.25 connection */
int
ax_parse(buf,cnt)
char *buf;
int16 cnt;
{
	struct mbuf *bp;
	register char *cp;
	char c;

	if(current == NULLSESSION || current->type != AX25TNC)
		return; /* "can't happen" */

	/* If recording is on, record outgoing stuff too */
	if(current->record != NULLFILE)
#if 0	/* recording to ASCII file - not used now */
		for (cp = buf; cp < (buf + cnt); cp++)
			if(*cp != '\r')
				fputc(*cp,current->record);
#else
		fwrite(buf,1,cnt,current->record);
#endif

	/* Allocate buffer and start it with the PID */
	if ((bp = alloc_mbuf(cnt+1)) == NULLBUF){
		printf(nospace);
		return;
	}

	*bp->data = PID_FIRST | PID_LAST | PID_NO_L3;
	bp->cnt++;

	/* Copy keyboard buffer to output, stripping line feeds */
	cp = bp->data + 1;
	while(cnt-- != 0){
		c = *buf++;
		if(c != '\n'){
			*cp++ = c;
			bp->cnt++;
		}
	}
	send_ax25(current->cb.ax25_cb,bp);
}

/* Handle incoming terminal traffic */
void
ax_rx(axp,cnt)
struct ax25_cb *axp;
int16 cnt;
{
	register struct mbuf *bp;
	struct mbuf *recv_ax25();
	char c;
	extern int flowctrl,ttydriv();

	/* Hold output if we're not the current session, and while typing */
	if(mode != CONV_MODE || current == NULLSESSION
	 || current->type != AX25TNC || current->cb.ax25_cb != axp
	 || (flowctrl && ttydriv(-1,NULL)))
		return;

	if((bp = recv_ax25(axp,cnt)) == NULLBUF)
		return;

	/* Display received characters, translating CR's to CR/LF */
	while(bp != NULLBUF){
		while(bp->cnt-- != 0){
			c = *bp->data++;
			if(c == '\r')
				c = '\n';
			putchar(c);
			if(current->record){
#ifndef UNIX
				if(c == '\n')
					fputc('\r',current->record);
#endif
				fputc(c,current->record);
			}
		}
		bp = free_mbuf(bp);
	}
	if(current->record)
		fflush(current->record);
	fflush(stdout);
}
/* Handle transmit upcalls. Used only for file uploading */
void
ax_tx(axp,cnt)
struct ax25_cb *axp;
int16 cnt;
{
	register char *cp;
	struct session *s;
	register struct mbuf *bp;
	int16 size;
	int c;

	if((s = (struct session *)axp->user) == NULLSESSION
	 || s->upload == NULLFILE)
		return;
	while(cnt != 0){
		size = min(cnt,axp->paclen+1);
		while((bp = alloc_mbuf(size)) == NULLBUF && size != 0)
			size /= 2;
		if(!size)
			if(axp->txq != NULLBUF)
				return;
			else
				break;
		cp = bp->data;
		/* Start with the PID */
		*cp++ = PID_FIRST | PID_LAST | PID_NO_L3;
		bp->cnt++;

		/* Now send data characters, translating between local
		 * keyboard end-of-line sequences and the (unwritten)
		 * AX.25 convention, which is carriage-return only
		 */
		while(bp->cnt < size){
			if((c = getc(s->upload)) == EOF)
				break;
#if	(defined(MSDOS) || defined(ATARI_ST))
			/* MS-DOS gives cr-lf */
			if(c == '\n')
				continue;
#endif
#if	(defined(UNIX) || defined(MAC) || defined(AMIGA))
			/* These give lf only */
			if(c == '\n')
				c = '\r';
#endif
			*cp++ = c;
			bp->cnt++;
		}
		if(bp->cnt > 1) {
			send_ax25(axp,bp);
		} else {
			/* Empty frame, don't bother sending */
			free_p(bp);
			break;
		}
		cnt -= bp->cnt;
	}
	if(cnt != 0){
		/* Error or end-of-file */
		fclose(s->upload);
		s->upload = NULLFILE;
		free(s->ufile);
		s->ufile = NULLCHAR;
	}
}

/* default state-change upcall handler for AX.25 ports */

void
ax_noserv(axp,old,new,msg)
struct ax25_cb *axp;
int old,new,msg;

{
	struct mbuf *qstring();
	struct ax25_call *axc;
	char mesg[256],tmp[10];
	void disc_ax25();

	if(new == CONNECTED && new != old){
	    pax25(tmp,axp->interface->hwaddr);
	    log_ax(axp,"No SERVER");
	    sprintf(mesg,"%c\rNo SERVER available.\rActive callsigns: %s",
			 PID_FIRST | PID_LAST | PID_NO_L3,tmp);

	    /* in addition to the interface address, present all callsigns of
	       ports connectable from this interface, which have an active
	       server. this should guide the innocent in selecting other
	       callsigns to get in contact with us..... */
	    for (axc = ax25_port; axc != NULLAXCALL; axc = axc->p_next){
		if(axc->mode == CONNECT && axc->s_upcall != ax_noserv &&
		   ((axc->flags & MULTI_IF) || axc->interface == axp->interface)){
		    pax25(tmp,&axc->addr);
		    strcat(mesg," ");
		    strcat(mesg,tmp);
		}
	    }
	    strcat(mesg,".\r");
	    /* must not call send_ax25 because it would send out the
	       message even before the UA was transmitted */
	    enqueue(&axp->txq,qstring(mesg));

	    /* force a disconnect after message transmission */
	    axp->t_upcall = axp->r_upcall = disc_ax25;
	}
}
