#include <fido.h>
#include <ascii.h>
#include <xfbuf.h>
#include <\mailer\fidomem.h>
#include <filexfer.h>

/*
	Various low level and extremely important support routines.

lidiv(l,i)	Divide long 'l' by short int 'i', and return as an
		int. If 'i' is zero, returns 0 as a result.

string(stringID) Return a pointer to the indicated string. See TEXT.H.

append(fname)	Open or create file fname for appending.

open_log(fname)	Open the specified NAMEn.EXT, where n is the task ID
close_log()	Close it.

close_up()	MSDOS kludge: when carrier is lost (and we stack jump
		out) we gotta close any open files else the handles
		stay busy and unavailable.

logoff(code,disc)
		Terminate this call; disconnect from the modem if
		disc is true; sets code as the DOS ERRORLEVEL for this
		connect. This writes out the caller record as necessary, 
		etc.

shared()	Returns true if this message area is shared ("O=Shared") or
		taskID is non-zero. 

maketid(buf,name) Creates filenames of the sort NAMEn.EXT, where n is
		the task ID; ie taskid = 9 makes FIDO9.LOG.

opentf(fn,mode)	Look for and open a file. If a language path is set,
		it tries there first; if none or open fails, it 
		uses the bbspath. Returns the file handle (-1 for failure)

makefname(buf,name);	file download (FidoNet outbound)
makeuname(buf,name);	file upload (FidoNet inbound)
makemname(buf,name);	.MSG files
maketname(buf,name);	.HLP files and .BBS files (ASCII)
makenname(buf,name);	nodelist IN NODESTUF.C
makesname(buf,name);	other system files (.LOG, .TLG, 
		Creates a full pathname from the current area or
		system file. NOTE: If 'ovpath' is not blank,
		it overrides the file and message paths.

getsys()	Loads a fresh copy of FIDO.SYS and set most global parameters;
		it does not set hardware-related ones like the serial port.

putsys()	Writes out the current system file.

bucks_str(n)	Generate a from a monetary value. 

fbit(bit)	Return true if FBIT is set. 

stoggle(n,v)	Set or clear switch #n. Switches can be either ON or OFF,
		or ONCE, a one-shot toggle. V: 0 to clear (OFF), 1 to 
		set (ON), -1 to set oneshot (ONCE).

istoggle(n)	Return toggle #N; 1 if (ON), -1 if a OneShot (ONCE), else
		0 if not set at all (OFF).

fcomp(s1,s2,n)	Fixed length string compare; bound to max. length N.

*/


/* Divide a long by an int, return as int. If the int is 0, return 0. */

lidiv(l,i)
long l;
int i;
{
long x;

	if (i) {
		x= i;			/* lattice 2.15 hates long arith */
		i= l / x;		/* divide */
	}
	return(i);			/* return (aargh) */
}

/* Return a pointer to the language string. */

char *
string(id)
unsigned id;
{
char **cp;

	cp= (char **) texttbl[(id >> _TBLID) - 1];	/* get addr of table, */
	cp= (char **) cp[id & _TBLIDM];			/* then addr of the string */
	return((char *) cp);
}

/* Return true if the FBIT is set. */

fbit(bit)
int bit;
{
	return(fido.fbits[bit / sizeof(char)] & (1 << bit % sizeof(char)));
}

/* Open a file for appending, creating it if necessary. Return the
open handle, or -1 if error. */

append(s)
char *s;
{
int h;
char c;

	h= open(s,2);				/* open or create the */
	if (h == -1) h= creat(s,2);		/* file, if opened OK */
	else lseek(h,0L,2);			/* seek to the end */
	return(h);				/* handle or error */
}

/* Open the log file. */

open_log(name)
char *name;
{

	if (cmtfile != -1) close_log();	/* other one may be open */
	cmtfile= append(name);		/* open sysop log */
	if (cmtfile == -1) {
		cprintf(SM+10,name);	/* "cant create %s" */
	}
}

/* Close the log file. */

close_log() {

	if (cmtfile != -1) close(cmtfile);
	cmtfile= -1;
}

/* Since we may have been aborted at any point, we probably
had open files. This is gross, but this closes all possible handles
except the ones we know of (log file, nodemap file)  */

close_up() {
int i;

	for (i= 0; i < 50; i++) {
		if (i == cmtfile) continue;	/* not the log file */
		close(i);
	}
}

/* Log off this call; disconnect, set return code, and
force termination with a frc_abort() to return to the main. */

logoff(code,disc)
int code,disc;
{
	limit= 0;				/* disable time limits */
	doscode= code;				/* set result code, */
	frc_abort();				/* force return to main */
}

/* Open the system file FIDO.SYS, and read the structure into memory; returns
-1 if error. */

getsys() {
int n;

	n= open("fido.sys",0);
	if (n == -1) return(-1);		/* oops! */
	read(n,&fido,sizeof(struct _fido));	/* load it */
	close(n);				/* close it */
	return(1);
}

/* Write out the system file. Note that this writes out only part
of FIDO.SYS. */

putsys() {
int n;

	n= open("fido.sys",2);			/* open for writing, */
	if (n == -1) return(n);
	write(n,&fido,(char *)&fido.cmd - (char *)&fido); /* write it out */
	close(n);
	return(n);
}

/* Round off a long integer to the nearest value and return as an int. */

nearestn(n,ro)
long n;		/* number to round, */
long ro;	/* divisor */
{
int i;
	i= (n + (ro - 1L)) / ro;	/* convert to integer */
	return(i);
}

/* Fixed length string compare. Returns 0 if a match */

fcomp(s1,s2,n)
char *s1,*s2;
int n;
{
	while (n) {
		if (*s1++ != *s2++) break;
		--n;
	}
	return(n);
}
/* Parse a string into net and node number. zone and net are untouched if
not found in the input string. This parses as follows: (xx means unchanged)

string			output

99:88/77		99:88/77.0
88/77			xx:88/88.0
77			xx:xx/77.0
99:88			99:88/00.0
88/			xx:88/00.0
99:88/77.3		99:88/77.3

*/

set_nn(cp,n)
char *cp;
struct _node *n;
{
int x;

	if (isdigit(*cp)) {
		n-> number= atoi(cp);		/* assume its node only */
		while (isdigit(*cp)) ++cp;	/* skip the number, */
	}
	if (*cp == ':') {			/* if it was a zone, */
		n-> zone= n-> number;		/* that was a zone number, */
		n-> net= atoi(++cp);		/* net must follow */
		while (isdigit(*cp)) ++cp;	/* skip the net number */
		if (*cp == '/') {		/* if that was a net number */
			n-> number= atoi(++cp);	/* set node number (or zero) */
			while (isdigit(*cp)) ++cp; /* skip node number */
		}

	} else if (*cp == '/') {		/* if that was a net number, */
		n-> net= n-> number;		/* copy it up, */
		n-> number= atoi(++cp);		/* now set node number */
		while (isdigit(*cp)) ++cp;	/* skip node number */
	}
	if (*cp == '.') {			/* if a dot */
		n-> point= atoi(++cp);		/* parse point */
	}
}

/* Return true if the two nodes are the same. */

same_node(n1,n2)
struct _node *n1,*n2;
{
	return(
		(n1-> number == n2-> number) &&
		(n1-> net == n2-> net) &&
		(n1-> zone == n2-> zone)
	);
}

/* Copy one node structure into another. IMPORTANT NOTE: Notice that they
structures are treated as char * pointers. */

cpy_node(d,s)
char *d,*s;			/* our code */
/* struct _node *d,*s;		REALITY */
{
int i;
	for (i= sizeof(struct _node); i--;)
		*d++= *s++;
}

/* Return a pointer to a string of the zone:net/node. */

char *str_node(n)
struct _node *n;
{
#define KLUDGE 4			/* strings we can make at once */
static char *cp,work[KLUDGE][40];	/* where we keep them */
static int k;				/* which one as are on */

	k = ++k % KLUDGE;	/* select next ... */
	cp= work[k];		/* easier faster shorter */
	*cp= NUL;		/* empty it */

	sprintf(cp,0,"%d:",n-> zone);
	sprintf(cp,0,(n-> point ? "%d:%d/%d.%d" : "%d:%d/%d"),
	    n-> zone,n-> net,n-> number,n-> point);
	return(cp);
}
/* Return true if this net/node combination is us. */

is_us(n)
struct _node *n;
{
	return(same_node(n,&id));
}

/*
	Various file transfer support routines

NOTE: The args to xferstat are really: xferstat(n,z,0,s); 
the 0 is always present so that we can point to it to force
Fido's _fspr() to assume an inline format string. 


xferstat(n,z,s)	Controls error reporting and status logging for
int n;		file transfers. N is the function call number;
long z;
char *s;	0 == Initialize: clears the file status counters
		totl_???? to zero, and readies the display.

		1 == start new file: Displays the passed filename (s)
		and overall file size (z).

		2 == status display: current block number, etc. Z
		is current byte offset. s is a runtime message.

		3 == error message: similar to above but can be
		handled differently if necessary.

badname(s)	Returns true if the passed ASCII name is a device
		name.

baudrate()	Returns the current baud rate.

fexist(fn)	Returns true if this file cannot be overwritten;
char *fn;	ie. if it exists and overwriting is not allowed by
		the program.

chkabort()	Checks the local keyboard and returns true if the
		file transfer should be aborted.
*/

/* Display a file transfer status message. N is how to handle it:

	0	Initialize display
	1	new filename (do CR/LF before and after)
	2	runtime status (do CR only)
	3	runtime error (do CR/LF)
 */

xferstat(n,z,s)
int n;		/* status message type */
long z;		/* block/offset/etc */
char *s;	/* string or format */
{
char *cp,buff[SS * 4];

	if (! fbit(FBIT_XFERDISP)) return;
	if (n == 0) return;

	switch (filemode) {
		case XMODEM:	cp= "X"; break;
		case XMODEMC:	cp= "XC"; break;
		case TELINK:	cp= "T"; break;
		case TELINKC:	cp= "TC"; break;
		case MODEM7:	cp= "B"; break;
		case MODEM7C:	cp= "BC"; break;
		case KERMIT:	cp= "K"; break;
		case DIETIFNA:	cp= "D"; break;
		case ZMODEM:	cp= "Z"; break;
		case FSC001X:	cp= "FX"; break;
		case FSC001T:	cp= "FT"; break;
		default: 	cp= "??"; break;
	}
	_fspr(buff,&s);
	cprintf(0,"-%s: %s %,lu     \r",cp,buff,z);
	if ((n == 1) || (n == 3)) cputs("\n");
/**/	if (n == 3) {
/**/		if (fcomp(s,"FSC001",6) == 0) {
/**/			lprintf(0,"! -%s: %s %,lu\r\n",cp,buff,z);
/**/		}
/**/	}
}

/* Check for illegal filenames. Mostly just device names. */

char *strip_path();

badname(s)
char *s;
{
int f,i;
char name[SS];
struct _xfbuf xfbuf;

	cpyarg(name,s);			/* clean copy, */
	stolower(name);			/* easy testing */
	xfbuf.s_attrib= 0xff;		/* all types */
	if (! _find(name,0,&xfbuf)) 	/* if it doesnt exist, */
		return(0);		/* no prob */

/* NOTE: This uses the direct INT 21 open() and close(), since ioctl() needs
the DOS handle, not our internal one. */

	f= _open(name,0);		/* if we can't open it */
	if (f == -1) return(0);		/* it aint there */

	i= _ioctl(0,f,0,0);		/* see if device */
	_close(f);			/* close handle */
	return(i & 0x80);		/* 0x80 is the 'device' bit */
}

/* Return true if we cannot overwrite this file. */

fexist(fn)
char *fn;
{
	return(0);
}

/* Check for local abort. */

chkabort() {

	if (bdos(6,0xff) == 3) doscode= 1;
	if (doscode == 1) return(1);		/* ie. not g-errorlevel, etc */
	return(0);
}

/* Return the current baud rate. */

baudrate() {

	return(datarate);
}
