#include <ascii.h>
#include "fido.h"
#include "fidomem.h"
#include "proto.h"

/*

Low level modem support. These depend on and set the following:

int mdmtype;		determines modem behavior. Nothing works if
			this is wrong.

FLAG cd_flag;		1 == ignore cd(). Disables carrier-loss
			detection. Used while initializing,
			dialing, disconnecting, etc.

unsigned cd_bit;	modem CD mask bit. 

unsigned datarate;	data baud rate, at which file transfer times
			and such are computed. It may be the same as
			the link rate.

unsigned rate;		modem baud rate; the speed at which Fido talks
			to the modem. For 300/1200/2400 type modems, it
			is the same as datarate.

unsigned online_baud;	if != 0, then just assume we are connected 
			at this rate; if the modem type indicates a
			fixed link rate, then this is used as the
			datarate only.

See statics below for mdmfunc. internal variables.


setbaud(r)
	Set the baud rate to r. For variable speed modems, this sets
	the data rate and the link rate the same. For fixed link rate
	modems it sets only the data rate; the link rate never changes.

answer()
	This is called while waiting for modem activity or an event.
	Answer the phone if it is ringing. This counts RING result
	codes, and issues an ATA command and waits for connect when 
	enough are found. 

	Returns:	-2	modem going idle (from previous state)
			-1	modem commands in progress (dont dial!)
			0	modem idle
			1	modem connected & online

	If answer() says connected, then true carrier can be watched. 

	If mdmstate == 1 and carrier is not present, the modem is
	assumed to be idle.

	NOTE: If the flag online_baud is non-zero answer() sets 
	that as the current baud rate, clears it, flushes modem garbage, 
	and returns == 1. This allows external shell programs to
	invoke Fido. It is assumed that the G-ErrorLevel will be
	used, as this works only one time. (Since it is cleared when
	used, to cover carrier-loss etc)

discon()
	Disconnects the modem and readies it for the next call. This does
	various obscure things to make lousy hobbiest modems perform more or
	less reliably. 

result= dial(s);
char *s;
		Dialer module for FidoNet. Given a string, dial the
	number, and return true if connected, else 0 if no connection.
	The phone number string is passed to the modem as-is, except
	for the following characters are translated:

	p	P
	t	T

chk_modem()	
	Poll the modem, and return any result code found or -1 if
	none received. This returns the numeric value of the result 
	code string. The buffer must be initialized to a null string
	the first pass through, and contents preserved between calls.

connect(result)
int result;
	Converts the raw modem result code into a modem state code
	that tells the "meaning" of the result. The code returned is:

		1  connected, baud rate set
		0  no change (status only)
		-1 command error, bad phone line, etc
		-2 no carrier, busy, etc

	For answer mode, 1 means an incoming call has completed, and
	is underway. All others means no action.

	For originate mode, while dialing, 1 means as above, 0 means
	status only (ringing, protocol notices, etc), (ie. keep waiting
	for a meaningful result), -1 means failure, -2 means merely
	the connection was not made.

char *lastconnect()
	Returns the string defining the last modem result.

*/

static int mdmstate;		/* what the modem is up to:
					0	idling
					1	online
					-1	awaiting result from the modem
					-2	going to idle state
				*/

static char ring_count;		/* number of rings in a row */
static long ring_timer;		/* millisecs at last RING result */

static char mdmbuff[SS] = "";	/* we store results here, etc */
static char mdmresult[SS] = "";	/* last modem result */

static long modem_tmr = 0L;	/* timer to reinit the modem */
static int h2 = -1;		/* handle for modem-init timer */

/* Send any modem initialization file to the modem. Return true if
we output anything to the screen. */

int modem_init(t)
int t;
{
int x,i,n;

/* Check to see if the phone is ringing. OK, so the modem might not be
initialized. What can I do? Probably it's OK if we get a RING result code. 
We check it first only to catch the common-case of first time through having
the line ringing, and missing the Initializing Modme message. */

	if (answer()) return(0);		/* ringing/in progress */

	if (h2 == -1) {
		h2= set_tick(&modem_tmr);	/* start timer first time */
		modem_tmr= (t * 60000L);	/* set to go off NOW */
		t= 0;				/* output a message... */
	}
	if ((modem_tmr / 60000L) < t) return(0); /* not time yet */

	if (! t) cprintf(SM+142);		/* "initializing the modem" */
	modem_tmr= 0L;				/* now it is -- reset it */
	limit= 0;				/* no limit to init the modem */
	clr_clk();				/* reset the clock, */

	mdmstate= 0;				/* modem is now idle */
	if (mdmtype == NOMODEM) return(0);	/* no modem at all */
	if (mdmtype == DIRECT) {
		discon();			/* lower/raise DTR */
		return(0);			/* no further initialization */
	}

/* Make sure the modem is not connected to anything before we attempt
to init it; toggling DTR usually puts most modems into command state if
they are not already. */

	cd_flag= 1;					/* ignore CD */
	modem_type();					/* set baud rate, etc */
	mflush();					/* flush all output, */
	lower_dtr();					/* drop DTR */
	delay(10);					/* for 100 mS, */
	raise_dtr();

/* The Telebit modems use a fixed link rate set by fiddling an S register.
Until that register is set, the modem may be assuming fixed 9600, fixed 19200
or variable rate; we have to determine which the hard way! So for now, attempt
AT commands at the desired rate, if no response, try the "other" rate (ie.
9600 or 19200), so that we can issue the proper commands. Once done, we can
later set the link rate to the correct rate. */

	if ( ((mdmtype == TBT2) || (mdmtype == TBT3))
	  && (n= linkrate)) {
		for (i= 4; i > 0; i--) {
			baud(n);			/* set link rate, */
			modout(CR);			/* flush garbage */
			flush(10);
			sendwt("ATV0\r");		/* (minimal init) */
			x= sendwt("AT\r");		/* try to get something */
			if (x >= 0) break;		/* if we got something exit */
			if ((n += n) != 19200) n= 9600;	/* toggle 9600/19200 */
		}

	} else {
		modout(CR);			/* clear the modem command buffer */
		flush(20);			/* clear the line */
	}
	modem_cmd();				/* send modem init commands */

/* The modem is now supposed to be completely initialized and ready to use. If
it was a fixed rate Telebit, we can now set the link rate to the correct speed.
This implies that extra initialization done by 'modem-string' cannot change
the link rate for these modems. */

	modem_type();			/* again set true linkrate/maxbaud */

	if (chk_modem() == 2) return(0); /* ringing! */
	if (*mdmstr) {			/* if additional initialization, */
		if (sendwt(mdmstr) == 2)/* send it, */
			return(0);	/* phone ringing! */
	}
	modem_chk();			/* put modem & computer at same rate */
	mdmstate= 0;			/* modem is now idle */
	return(!t);			/* 1 == we output a msg */
}

/* Issue initialization commands for this modem. */

static modem_cmd() {
char *cp;

	atp("ATV0Q0E0S0=0M0\r");		/* issue basic commands, */
	flush(0);				/* flush echo & result, */

	cp= "";
	switch (mdmtype) {
		case HAYES3:	break;

		case HAYES: 
		case USR: 
		case POP24:	cp= "ATX1\r"; break;	/* generic 1200 */
		case USR24:	cp= "ATX3\r"; break;	/* generic 2400 */

		case POPCOM: 	cp= "ATX1S18=0\r"; break;
		case HAYES24:	cp= "ATX3&D2&C1\r"; break;
		case GDC: 	cp= "ATX1S17=3Z\r"; break;
		case MT24:	cp= "ATB0&I0&Q1X3\r"; break;

		case USR24H:
		case HST: 	cp= "ATX3&H1&R2\r"; break; 

		case HST2: 
		case HST3: 
		case HST4: 	cp= "ATX3&H1&R2&B1\r"; break;

		case DST1:
		case DST2:	cp= "ATX3&H1&R2&A2&B1\r"; break;

		case TBT: 	cp= "ATS51=255S66=0X3S45=0S54=3S53=1S52=1S58=2\r"; break;
		case TBT2:	cp= "ATS51=4S66=1X3S45=0S54=3S53=1S52=1S58=2\r"; break;
		case TBT3:	cp= "ATS51=5S66=1X3S45=0S54=3S53=1S52=1S58=2\r"; break;

		case SMV2:
		case SMV3:	cp= "AT&Q5S36=3S50=140W1X3&C1&D2&K3S37=9\r"; break;
		case SMV1:	cp= "ATW1X3&C1&D2&K3S37=9\r"; break;

		default:	cp= "ATX1\r"; break;
	}
	if (*cp) sendwt(cp);
}

/* Output a modem command string, wait for the result code, or a timeout
after 4 seconds. Returns the result code or -1 if timeout or modem activity. */

static sendwt(s)
char *s;
{
int n;
long til;

	if (chk_modem() == 2) return(2); /* check for line ringing */
	atp(s);				/* output the string */
	til= ((taskid | fido.mtasker) ? 3000L : 1000L);	/* 1 sec, or 6 if multitasker */
	til += millis2;			/* until now plus that */
	while (millis2 < til) {
		n= chk_modem();		/* check for result, */
		if (n >= 0) {		/* stop if we get one */
			delay(25);
			break;
		}
	}
	return(n);
}

/* Send a command sequence to the modem, abort if we detect 
incoming activity. */

static atp(s)
char *s;
{
	while (*s) {
		modout(*s++);
		delay(4);
	}
	return(0);
}

/* Set the modem type and max. baud rate. */

void modem_type() {

	linkrate= 0;			/* assume variable link rate */
	switch (mdmtype) {
		case NOMODEM:
		case DIRECT: break;	/* leave maxbaud untouched */

		case SM: 
			maxbaud= 300; break;

		case HAYES: 
		case USR:
		case POPCOM:
		case GDC: 
			maxbaud= 1200; break;

		case HAYES24: 
		case USR24: 
		case USR24H: 
		case MT24: 
		case POP24:
			maxbaud= 2400; break;

		case TBT:		/* variable */
		case HST:		/* variable */
		case SMV1:		/* variable */
			maxbaud= 9600; break;

		case TBT2:		/* fixed */
		case HST2:		/* fixed */
		case SMV2: 		/* fixed */
		case DST1:		/* fixed */
			linkrate= maxbaud= 9600; break;

		case TBT3:		/* fixed */
		case HST3:		/* fixed */
		case SMV3:		/* fixed */
		case DST2:		/* fixed */
			linkrate= maxbaud= 19200; break;

		case HST4:		/* fixed */
			linkrate= maxbaud= 38400; break;

		case VARF:		/* fixed */
			linkrate= fido.maxbaud; break;
	}
	if (fido.maxbaud) maxbaud= fido.maxbaud; /* possible override */
	if (linkrate) baud(linkrate);	/* set fixed-link baud rate */
	setbaud(maxbaud);		/* data rate (or physical) to max */
}

/* Set the desired baud rate. Bound it to the maximum the modem can handle. 
This sets the physical link data rate only if not a fixed rate modem. Returns
the data rate actually set. */

setbaud(n)
unsigned n;
{
	if (n > maxbaud) n= maxbaud;		/* bound it */
	datarate= n;				/* set data rate, */

	if (! linkrate) baud(datarate);		/* set link rate if needed */

	return(datarate);
}

/* Disconnect. First, it delays to let (output buffered) characters out,
then drops DTR and waits for carrier loss. (Timeout if it does not
go away.) A delay is done to ensure that stupid modems see DTR and dont 
get upset. Then DTR is raised. */

void discon() {

int i,a;

	cd_flag= 1;				/* disable carrier-loss detection */

	a= (mdmtype == DIRECT) || (mdmtype == NOMODEM);
	limit= 0;				/* no time limit, we check explicitly */

/* Wait until the output buffer is flushed. If carrier is lost, flush
anything that remains and stop waiting. Output a CR to flush the modems
command buffer in case anything went out with CD low. */

	while (_cd() && _mbusy());		/* while carrier & outputting */
	delay(10);				/* wait 100 mS (buffered modems) */
	mflush();				/* flush anything remaining */

	if (! a) modout(CR);			/* abort commands for "AT" modems */

/* With DTR low, wait for CD to go away. If it doesnt, complain like hell,
and wait for a Control-C, then quit everything. This aborts dialing also
for some modems. */

	lower_dtr();			/* drop DTR, */
	delay(50);			/* wait 100 mS minimum the first time */
	for (i= 10; i--;) {		/* wait for no CD */
		delay(50);		/* 50 mS minimum, */
		if (! _cd()) break;
	}
	raise_dtr();			/* enable the modem again */
	delay(10);			/* short delay for DTR */

/* If that didnt work, try +++; if there is still carrier (USR disconnects 
on +++) issue ATH0 (which will disconnect the Hayes etc) */

	if (!a && _cd()) {		/* if STILL carrier,*/
		for (i= 5; i--;) {	/* try +++ */
			flush(0);
			sendwt("+++");	/* try to disconnect */
			if (! _cd()) break; /* stop if disconnected, */

			sendwt("ATH0\r"); /* try H0 next ... */
			if (! _cd()) break; /* did that work??? */
		}
	}

/* It better be disconnected by now! */

	if (_cd()) {				/* if still connected */
		doscode= 1;			/* fatal error */
		cprintf(SM+185);
		cprintf(SM+186);
		cprintf(SM+187);
		cprintf(SM+188);		/* msg to the sysop */
		cprintf(SM+189);
		cprintf(SM+190);
		cprintf(SM+191);
		cprintf(SM+192);

/* Since this is probably called from within logoff(), doscode is already set;
this makes sure that Fido will terminate after Control-C. */

		doscode= 1;

		while (1) if (bdos(6,0xff) == ETX) return;
	}

/* This fixes lots of problems with the stupid Hayes modems not answering at
2400 baud. We don't do it if we were invoked via nnnnn/O (Presumably the caller
handles the modem). */

	setbaud(maxbaud);			/* make modem ready, at max baud */
	if (! online_baud) modem_chk();		/* tell modem new baud rate */
	mdmstate= 0;				/* modem is now idle */
	cd_flag= 0;				/* allows CD loss detection */
}

/* Issue AT commands a few times and try to get the modems attention. */

static modem_chk() {

int i;

	if ((mdmtype == DIRECT) || (mdmtype == NOMODEM)) return;

	for (i= 3; i--; ) {
		if (sendwt("AT\r") >= 0) return; /* get some response */
	}
	cprintf(SM+193);			/* "modem not responding" */
}

/* Dial a numeric string. Returns: 0 for no connection, 1 for connected,
-1 for error, such as voice or no dial tone. The speed to dial at is assumed
to be set before calling. This returns with the actual connect rate set
if a connection is made. */

dial(string)
char *string;
{
char c,*p,*cp;
int n;
char in[SS],out[SS];

	cd_flag= 1;			/* disable carrier-loss detection */

	strcpy(out,"ATDT");		/* output string so far, */
	in[0]= NUL;			/* input string so far */
	if (*dial_pref) {		/* if a prefix is specified */
		strcat(in,dial_pref);	/* add it, */
		strcat(in," ");		/* add a space */
	}
	strcat(in,string);		/* add phone number (sic) to input */

	cp= &out[strlen(out)];		/* cp == end of dialing string so far */
	p= in;				/* p == input string */
	while (*p) {
		c= *p++;
		switch (c) {
			case 't': c= 'T'; break;	/* lower to upper case */
			case 'p': c= 'P'; break;

/* 			case '$': return(script(p)); */	/* execute the script */
		}
		if (c != NUL) *cp++= c;
	}
	*cp++= '\r';			/* terminate the string, */
	*cp= NUL;			/* null terminate it */

	limit= 2;
	clr_clk();			/* two minutes to dial */
	atp(out);			/* issue the command string */
	flush(10);			/* flush in case echo is on */

/* Now wait for a result. Note that if we detect an incoming ring, it
will terminate the dial with a no-connection, and presumably the main
loop will detect the incoming call on the next ring. */

	while (1) {			/* wait for a connect, no connect or error */
		limitchk();		/* check for timeout */
		if (bdos(6,0xff) == ETX) { /* check for Control-C */
			clprintf(SM+194); /* abort dial attempt */
			discon();	/* full disconnect */
			return(0);	/* no connection */
		}
		n= chk_modem();		/* get the result code, */
		if (n < 0) continue;	/* no result complete yet */
		n= connect(n);		/* got gone; process it, */
		if (n != 0) break;	/* got a meaningful result */
	}
	if (n == -2) n= 0;		/* make NO CONNECT non-failure */
	else if (n == 1) delay(200);	/* delay for telco */
	return(n);
}

/* Poll the modem looking for activity; returns a code indicating whats
happening. If 0, the modem is idle, awaiting calls etc. -1 means a 
RING was received, and ATA issued, and we're waiting for connect/fail.
1 means we are connected and online. */

answer() {
int n;
long til;

	cd_flag= 1;				/* fake carrier */

	if (online_baud) {			/* if exec'ed from a shell, */
		setbaud(online_baud);		/* assumed baud rate */
		mflush();			/* clear garbage */
		goto state1;			/* go set state */
	}

/* Handle the current modem state first. */

	switch (mdmstate) {

/* mdmstate == 1: Online and connected. If carrier lost, set going-idle
state -2, otherwise no change. */

		case 1:
state1:			mdmstate= 1;		/* (sometimes redundant) */
			if (! cd()) {		/* if connection lost */
				cd_flag= 1;
				mdmstate= -2;	/* modem is idle, */
			}
			return(mdmstate);

/* mdmstate == 0 (modem offline/idle) or -1 (offline/connect in progress).
These two states are actually handled by the code following this switch. */

/*		case 0:			*/	/* is now idle */
/*		case -1:		*/	/* command in progress */
/*			break;		*/	/* handled below */

/* mdmstate == -2: modem just went idle. This state is mainly for the main
loop to know when to issue the "Waiting for call or event" message; it
is immediately followed by a transition to state 0. */

		case -2:			/* went idle */
			mdmstate= 0;		/* is now idle */
			return(mdmstate);
	}

/* This is really the state for mdmstate == 0 or -1. Look for a change to
an active state. If a hardware connection, ie. DIRECT modem, when CD
goes true assume online state. */

	if (mdmtype == DIRECT) {		/* direct connect assumes */
		if (_cd()) goto state1;		/* CD indicates connect status */
	}

/* Here we actually poll the modem for a numeric result code. If nothing is
received from the modem (or all the result-code-characters are not received
yet) the state does not change. */

	n= chk_modem();				/* check for a result, */
	if (n < 0) return(mdmstate);		/* none yet, same state */

/* The modem has just issued a result code -- we may change to a 
new state. Process it (handles the different modem types, takes care of baud 
rate, etc) and see what to do: */

	switch (connect(n)) {			/* see what happened, */

/* Modem says "CONNECT", wait for the CD line to reflect this (or a timeout!)
then handle as if we're online. */

		case 1:				/* dial sucessful */
			for (til= millis2 + 20000L; millis2 < til;) {
				if (_cd()) break;
			}
			goto state1;		/* go set/check it */
 
/* Modem says connection not made (no carrier, etc) or status-only (ring). If
RING, after the right number of rings issue the ATA command to make the modem
answer, set mdmstate == -1 to indicate in-progress. For all other results
set mdmstate == -2, going-idle. */

		case -1:			/* dialing/command error (oops) */
		case -2:			/* "no carrier" (busy?!) */
		case 0:				/* status only */
			if (n == 2) {		/* RING! */
				if ((millisec - ring_timer) > 10000L) ring_count= 0;
				ring_timer= millisec; /* restart timer */
				if (++ring_count < rings) break;
				cprintf(SM+196);/* "attempting a connection" */
				flush(20);	/*   wait til its ready (& flush RINGs) */
				atp("ATA\r");	/*   force answer if RING */
				mdmstate= -1;	/*   somethings happening */

			} else {		/* not RING, */
				ring_count= 0;	/* clear the count */
				mdmstate= -2;	/*   so now idle */
			}
			break;
	}
	return(mdmstate);			/* say what it was */
}

/* Display the proper message for each possible result code, and return:

	1	connected and baud rate set
	0	no change of state (result code was status only)
	-1	error (unexpected condition or result code)
	-2	no connection (busy, etc)

	NOTE: Result code 2 is assumed to be incoming "Ring". 

	answer() checks for (return code -2 / result code 2) and
	issues appropriate answer-phone commands.

	dial() checks specifically for (return code -2 / result code 2) to
	detect incoming rings.
*/

static struct {
	unsigned code;			/* code from the modem */
	int retcode;			/* our return code */
	char *string;			/* local message */

} results[] = {
	1,300,"",
	2,-2,"Ring!",
	3,-2,"No Carrier",
	4,-1,"Command error!",
	5,1200,"",
	6,-1,"No Dial Tone!",
	7,-2,"Busy",
	8,-2,"No Answer",
	9,600,"",
	10,2400,"",
	11,0,"Ringing!",
	12,-1,"Voice!",
	13,9600,"",
	14,300,"/ARQ",
	15,1200,"/ARQ",
	16,2400,"/ARQ",
	17,9600,"/ARQ",
	18,4800,"",
	20,300,"/REL",
	22,1200,"/REL",
	23,2400,"/REL",
	40,0,"Carrier at 300",
	46,0,"Carrier at 1200",
	47,0,"Carrier at 2400",
	48,0,"Carrier at 4800",
	50,9600,"/FAST",
	52,0,"R-R-Ring!",
	61,9600,"/FAST/KERMIT",
	62,9600,"/FAST/XMODEM",
	63,9600,"/FAST/UUCP",
	70,0,"No protocol",
	71,0,"Protocol LAP-B",
	72,0,"Protocol LAP-B/Hdx",
	73,0,"Protocol AFT",
	10014,19200,"",
	10050,0,"Carrier at 9600",

	20023,4800,"/HST",		/* HST/DST codes */
	20030,7200,"/HST",
	20031,12000,"/HST",
	20035,14400,"/HST",
	20027,9600,"/ARQ/HST",
	20028,4800,"/ARQ/HST",
	20032,12000,"/ARQ/HST",
	20034,7200,"/ARQ/HST",
	20036,14400,"/ARQ/HST",
	20033,9600,"/V32",
	20038,4800,"/V32",
	20040,7200,"/V32",
	20041,12000,"/V32",
	20045,14400,"/V32",
	20037,9600,"/ARQ/V32",
	20039,4800,"/ARQ/V32",
	20044,7200,"/ARQ/V32",
	20042,12000,"/ARQ/V32",
	20046,14400,"/ARQ/V32",

/*	old HST/DST
	20023,9600,"/HST",
	20027,9600,"/ARQ/HST",
	20028,4800,"/HST",
	20029,4800,"/ARQ/HST",
	20033,9600,"/V32",
	20037,9600,"/ARQ/V32",
	20038,4800,"/V32",
	20039,4800,"/ARQ/V32",
*/
	0,0,""			/* code == 0 means end of table */
};

static connect(result)
int result;
{
int n;
char *cp;

	*mdmresult= NUL;			/* assume no modem status string */

/* Resolve conflicts in result code values by translating to a different
range. */

	switch (mdmtype) {
		case SMV1: case SMV2: case SMV3: /* Hayes V-Series */
			switch (result) {
				case 12: result= 13; break; /* conflicts with HST */
				case 11: result= 18; break; /* conflicts with HST */
				case 14: case 50: result += 10000; break;
			}
			break;

/* HST/Dual Std codes conflict with other codes -- translate them to 200xx
so they don't conflict. Then look them up. */

		case HST: case HST2: case HST3: case HST4: case USR24H:
		case DST1: case DST2:
			switch (result) {
				case 23: case 27: case 28: case 30:
				case 31: case 32: case 33: case 34:
				case 35: case 36: case 37: case 38:
				case 39: case 40: case 41: case 42:
				case 44: case 45: case 46:
					result += 20000; break;
			}
			break;
	}
	if (! result) return(0);		/* ignore OK result code */

	for (n= 0; results[n].code; n++)	/* find the result code */
		if (results[n].code == result) break;

	cp= results[n].string;			/* pull out the string, */
	n= results[n].retcode;			/* translate to return code */

	if (!n) sprintf(mdmresult,SM+197,result); /* unknown result code */

/* Do some setup if we are connected. */

	if (n > 0) {
		setbaud(n);			/* set baud rate, */
		delay(20); mflush(); 		/* flush trash */
		sprintf(mdmresult,SM+198,datarate,cp); /* "connected at 9600/HST" */
		n= 1;				/* return as flag */

	} else if (*cp) strcpy(mdmresult,cp);	/* else a status result */

	if (*mdmresult) {
		cputs(" * "); 
		cputs(mdmresult);		/* display it */
		cputs("\377");
	}
	return(n);
}

/* Return the last connect string. */

char *lastconnect() {

	return(mdmresult);
}

/* Check the modem for a result code, return its numerical value or -1
if none yet. This uses the modem buffer to store characters between
iterations. */

static chk_modem() {
#define i (mdmbuff[0])		/* the index into the buffer */
#define buff (&mdmbuff[1])	/* what we use as the buffer */

	while (_mconstat()) {			/* if a character there, */
		buff[i]= _mconin() & 0x7f;	/* get it, */
		if (buff[i] == CR) {		/* if a complete line, */
			i= 0;			/* empty the line, */
			if (isdigit(*buff)) return(atoi(buff));	/* return the result */
			else return(-1);	/* empty line?! */

		} else if (isdigit(buff[i])) {	/* if a new digit, */
			++i;			/* install it, */
		}
		buff[i]= NUL;			/* terminate string & erase non-digit */
	}
	return(-1);				/* nothing happened */
}
