#include "fido.h"
#include <ascii.h>
#include <xfbuf.h>
#include "fidomem.h"
#include "proto.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 this area is shared. */

shared() {

	return (taskid || (msgarea.flags & AREA_SHARED));
}

/* Clear toggle N if it is a one shot. */

void stoggle(n,v)
int n,v;
{
	n= 1 << n;				/* bit mask */

	fido.switches &= ~n;
	fido.oneshots &= ~n;			/* turn (OFF) both */

	if (v > 0) fido.switches |= n;		/* switch (ON) */
	else if (v < 0) fido.oneshots |= n;	/* oneshot (ONCE) */
}

/* Return toggle #N. */

int istoggle(n)
int n;
{
	n= 1 << n;				/* it's easier to type */
	if (fido.oneshots & n) return(-1);	/* oneshot set, (ONCE) */
	return((fido.switches & n) ? 1 : 0);	/* switch (ON) or (OFF) */
}

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

int 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. */

int 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. */

void open_log(name)
char *name;
{
char buff[SS];

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

/* Close the log file. */

void 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)  */

void close_up() {
int i;

	closemsg();				/* close any open message */
	for (i= 0; i < 50; i++) {
		if (i == cmtfile) continue;	/* not the log file */
		if (i == nodefile) continue;	/* not the node file */
		if (i == idxfile) continue;	/* not the node index file */
		if (i == ndxfile) continue;	/* not the node index file */
		if (i == zdxfile) continue;	/* not the node index file */
		if (i == ndatfile) continue;	/* not the node data file */
		if (i == ntcfile) continue;	/* not the node to call file */
		close(i);
	}
}

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

void logoff(code,disc)
int code,disc;
{
	limit= 0;				/* disable machines */
	doscode= code;				/* set result code, */
	if (!test && disc) {
		discon();			/* do disconnect, */
	}
	frc_abort();				/* force return to main */
}

/* Create a filename NAMEn.EXT, where n is the task id. If task ID is zero,
dont insert anything. */

void maketid(buff,str)
char *buff,*str;
{
	strcpy(buff,fido.syspath);		/* add system path */
	while (*buff) ++buff;			/* concatenate */

	while (*str) {
		if (*str == '.') break;		/* copy up to the dot */
		*buff++= *str++;
	}
	if (taskid) {				/* if non zero task, */
		sprintf(buff,0,"%d",taskid);	/* append the number, */
		while (*buff) ++buff;		/* find the end, */
	}
	while (*buff++= *str++);		/* copy the rest */
}

/* Open a file; look first in the language path, then in the text path. */

int opentf(fn,m)
char *fn;		/* filename */
int m;			/* mode */
{
char buff[SS];
int f;

	if (*langpath) {			/* if a language path set, */
		makexname(buff,langpath,fn);	/* make the full filename, */
		f= open(buff,m);		/* try the open, */
		if (f != -1) return(f);		/* success! */
	}
	makexname(buff,fido.textpath,fn);	/* use text path */
	return(open(buff,m));			/* return open result */
}

/* Assemble a text filename -- .HLP or .BBS (ASCII) files. */

void maketname(p,s)
char *p,*s;
{
	makexname(p,fido.textpath,s);
}

/* Assemble a full download filename. */

void makefname(p,s)
char *p,*s;
{
	if (*ovpath) makexname(p,ovpath,s);
	else makexname(p,filearea.path,s);
}

/* Assemble a system filename. */

void makesname(p,s)
char *p,*s;
{
	makexname(p,fido.syspath,s);
}

/* Assemble a full upload filename. */

void makeuname(p,s)
char *p,*s;
{
	if (*ovpath) makexname(p,ovpath,s);
	else makexname(p,filearea.upath,s);
}

/* Assemble a message filename. */

void makemname(p,s)
char *p,*s;
{
	if (*ovpath) makexname(p,ovpath,s);
	else makexname(p,msgarea.path,s);
}

/* Copy the path prefix, append one filename from the input string. */

void makexname(d,p,s)
char *d,*p,*s;
{
	while (*d= *p) {		/* copy in the path name, */
		++d; ++p;
	}
	cpyarg(d,s);			/* add the one filename */
}

/* Get message area N, return 0 if error. */

int getmarea(n)
unsigned n;
{
	return(getarea(n,&msgarea,0));
}

/* Get file area N, return 0 if error. */

int getfarea(n)
unsigned n;
{
	return(getarea(n,&filearea,1));
}

/* Get message or file area #n; return 0 if not set, illegal number
or other error. */

int getarea(n,a,file)
unsigned n;		/* area number */
struct _area *a;	/* struct to load */
int file;		/* 1 == get a file area */
{
int f,i;
long off;

	*ovpath= NUL;					/* always remove override */

	if (n < 0) return(0);				/* no such area */
	off= 0L + sizeof(struct _fido);			/* offset to beg msg areas */
	if (file) {					/* if a file area, */
		if (n >= fido.farea_max) return(0);	/* bound it */
		off += (long)(sizeof(struct _area) * fido.marea_max); /* beg file areas */

	} else if (n >= fido.marea_max) return(0);	/* msg area max */

	f= open("fido.sys",0);
	if (f == -1) return(0);				/* no system file! */

	off += (long)(n * sizeof(struct _area));	/* offset plus area # */
	lseek(f,off,0);					/* seek there, */
	i= read(f,a,sizeof(struct _area));		/* read some, */
	close(f);					/* immediately close it */

	if (i != sizeof(struct _area)) return(0);	/* check read error */
	a-> number= n;					/* set the path number */
	return(*a-> path != NUL);			/* invalid if no path set */
}

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

int 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. */

int 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);
}

/* Display money. Since this returns a string, we use a static string
array and can make no more than 4 at a time. */

char *bucks_str(b)
unsigned b;
{
static char bucks_str[4][8],bucks_i;		/* string "$327.67", 4 each, plus index, */

	if (++bucks_i > 3) bucks_i= 0;		/* next string ... */

	sprintf(bucks_str[bucks_i],0,"%c%d%c%02d",
	    fido.bucks,				/* dollar char, */
	    b / 100,				/* dollars, */
	    fido.dec_char,			/* the separator, */
	    b % 100);				/* cents, */
	return(bucks_str[bucks_i]);
}

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

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 */

int fcomp(s1,s2,n)
char *s1,*s2;
int n;
{
	while (n) {
		if (*s1++ != *s2++) break;
		--n;
	}
	return(n);
}
