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

/*
	caller interface support routines and such:

stats()		caller command: lists various caller status

setovpath()	Set the override path non-blank. Used by both
		files and messages.

extlogin(s)	Returns true if the string contains an "external login"
		sequence.

wordlen(cp,col)	Returns the length of the string as it would be displayed
		on the screen, taking into account tabs in the string
		and the current column.

uval(mask,shift)
		Returns the value from the caller bit record as an 
		integer. See the _clr struct def in fido.h

setuval(val,mask,shift)
		Sets a value in the caller bit record.

strsum(s)
		Returns the checksum of a string.

goodbye(yn)	This is the caller G)oodbye command; it is also used to
		do forced logoffs of the current caller due to timeouts,
		etc. The yn parameter is a string, "y" or "n" (sic) to
		flag whether to ask for a G)oodbye message or not; if
		null "" then it asks for the answer from the input
		command system.

dispbbs(fn)	Dumps text file fn.BBS from either the bbs-path or
		graphics-path depending on the caller record setting.

dumptext(f)	Displays an open text file on the callers screen,
		and makes it conform to screen width, wrapping
		text as necessary. NOTE: This assumes the file was
		opened with open().

		Control characters:

		soft-CR	ignored
		LF	ignored
		^A	IFNA Kludge; suppresses output til next CR
		^B	disables Control-C
		^C	reenables Control-C
		^D	immediate "more"
		^E	disable word wrap
		^F	enable word wrap
		^X	disables word wrap til next CR
		CR	end of line/paragraph

*/

/* Display the area contents. */

void dsparea(name,area,c)
int name;
struct _area *area;
char c;
{
	if (name) {
		mprintf(name); 				/* eg. "FidoNet" or NUL */
		mputs(" ");
	}
	mprintf(0,"%3d%c ",area-> number + 1,c);	/* "1*" or "1 " */
	if (fbit(FBIT_PATHDISP)) mprintf(0,"%-30s",area-> path); /* area name */
	highlight();					/* turn on highlighting */
	mputs(area-> desc);
	unhighlight();
	mcrlf();
}

/* List the callers statistics. Displays File Info only if F is true. */

void stats(f)
int f;
{

char buff[SS];
unsigned i[4],n;
long n1,n2;
char c1,c2;

	gtod(buff);
	mcrlf(); mprintf(0,"%s\377",buff);

	mprintf(CM+141);				/* table header */
	mprintf(CM+142,fido.callers);
	mprintf(CM+143,caller.times);  

	n= user_limit - minutes; if (n < 0) n= 0;
	mprintf(CM+144,minutes,user_limit,n);

	n= dlimit - caller.time; if (n < 0) n= 0;
	mprintf(CM+145,dlimit,n);

	if (! f) {
		mprintf(CM+146,bucks_str(caller.credit),bucks_str(caller.credit - caller.debit));
		return;
	}

	if (filearea.flags & AREA_UPLD) makeuname(buff,"");
	else makefname(buff,"");		/* use correct path */
	n= 0;					/* drive number, */
	if ((strlen(buff) >= 2) && (buff[1] == ':')) n= toupper(buff[0]) - 'A' + 1;
	_free(n,i);				/* get disk stuff, */
	n1= i[3]; n1 *= i[2]; n1 *= i[0];	/* total clust * bytes/sec * secs/clust */
	n2= i[1]; n2 *= i[2]; n2 *= i[0];	/* free clust * bytes/sec * secs/clust */
	c1= c2= 'K'; 
	n2 += 1023L; n2 /= 1024L;		/* make K bytes free */
	n1 += 1023L; n1 /= 1024L;		/* make K bytes total */
	if (n1 > 9999L) {			/* if really huge, */
		c1= 'M';			/* make Meg bytes */
		n1 /= 1024L;
	}	
	if (n2 > 9999L) {
		c2= 'M';
		n2 /= 1024L;
	}	
	mprintf(CM+147,n1,c1,n2,c2);		/* disk space */

	c1= 'K'; 				/* assume K bytes */
	n1= caller.dnld;
	if (n1 > 9999L) {			/* if really huge, */
		c1= 'M';			/* make Meg bytes */
		n1 /= 1024L;
	}	
	mprintf(CM+148,n1,c1);			/* "downloaded..." */

	n= klimit - caller.dnldl; if (n < 0) n= 0;
	mprintf(CM+149,klimit,n);		/* daily limit */

	n= klimit - caller.dnldl; if (n < 0) n= 0;
	n1= caller.upld;			/* total UL, K bytes */
	c1= 'K'; 				/* assume K bytes */
	if (n1 > 9999L) {			/* if really huge, */
		c1= 'M';			/* make Meg bytes */
		n1 /= 1024L;
	}	
	mprintf(CM+150,n1,c1);			/* uploaded */
}

/* Change the override path for files. */

void setovpath() {

char buff[SS],*cp;

	*ovpath= NUL;				/* kill current one */
	cp= getarg(CM+264);			/* "enter pathname" */
	cpyatm(buff,cp);			/* a copy of the path, */
	buff[PS]= NUL;				/* truncate to fit */
	if (! isdir(buff)) {			/* if its not valid, */
		if (nverf(CM+267)) mkdir(buff);	/* ask to create it */
		else return;			/* make it or quit now */
		if (! isdir(buff)) {
			mprintf(CM+151);		/* cant! */
			return;
		}
	}
	fixdir(buff);				/* append / *.* */
	strip_path(ovpath,buff);		/* copy prefix only */
	filearea.flags |= AREA_OVW | AREA_UPLD;	/* allow overwriting */
}

/* Return true if we find "external login" in this string. */

int extlogin(s)
char *s;
{
char buff[SS];

	cpyarg(buff,s);			/* concatenate the two */
	s= next_arg(s);			/* first words */
	cpyarg(&buff[strlen(buff)],s);	/* see if it is */
	stolower(buff);			/* "ExternalLogin" */
	return(same(buff,"externallogin"));
}

/* Return the value from the caller bit record. Since there is no equate
or other sensible system for the resolution of 4 used in CLR_WID, we
hard code the value here, since its pretty much hardcoded anyways. */

int uval(mask,shift)
int mask,shift;
{
int n;

	n= (caller.stuff >> shift) & mask;
	return(n);
}

/* Set a field in the caller bit record. */

void setuval(val,mask,shift)
int val,mask,shift;
{
long n;

	n= mask; n <<= shift;			/* we NEED long arith */
	caller.stuff &= ~n;			/* remove old value */

	n= val & mask; n <<= shift;		/* set it THEN shift it */
	caller.stuff |= n;			/* add in new value */
}

/* Create the checksum of a string. */

int strsum(s)
char *s;
{
int n;

	for (n= 0; *s;) n += *s++;
	return(n);
}

/* Display a text file from the BBS path. */

int dispbbs(filename)
char *filename;
{
int f;
char buff[SS];

	f= opentf(filename,0);			/* find/open file, */
	if (f == -1) return(0);			/* no such */
	dumptext(f);
	close(f);
	return(1);
}

/* Display the contents of a text file on the screen, do word wrap
and all the other good stuff. The handle of an open file is passed. Control-C
is ignored if a Control-B is found, and enabled again if a Control-C is 
found. This does not output lines beginning with ^A except for sysop level. */

void dumptext(f)
int f;					/* open file to dump to screen */
{
int screen;				/* current screen column */
int width;				/* callers maximum screen width */
int count;
char *cp;
char word[SS];				/* word we build before output */
int i;					/* index (length) of word */
char c,lastc;				/* current and previous character */
FLAG skipline;				/* 1 == dont output this line */
FLAG margrel;				/* 1 == "margin release" */
FLAG word_wrap;				/* 1 == disable word wrap */
FLAG page_pause;			/* 1 == disable "more" */

	if (f == -1) return;				/* be serious */

	width= uval(CLR_WID,CLR_WIDS);			/* do pretty margins */
	if (width > 40) width -= 2;
	else width -= 1;

	screen= 					/* nothing on screen yet */
	  skipline= 					/* start line suppression machine */
	  margrel= 					/* formatting is on */
	  word_wrap= 					/* do word wrap */
	  page_pause= 0;				/* do auto-"more" */

	c= CR;						/* in case first is SOH */
	while (! abort) {				/* (keyboard abort) */
		word[i= 0]= NUL;			/* word is empty */
		while (i < width) {			/* word at a time */
			lastc= c;			/* remember last character */
			while (1) {
				count= read(f,&c,1);	/* read a character, */
				if (! count) break;	/* check EOF */

				switch (c) {
					case EOT: more(); c= NUL; break;
					case SUB: c= NUL; count= 0; break;

					case CR: margrel= 0; break;
					case CR + 128: if (lastc == ' ') c= NUL;
						else c= ' '; break;

					case CAN: margrel= 1; c= NUL; break;
		    			case ENQ: word_wrap= 1; c= NUL; break;	/* off */
					case ACK: word_wrap= 0; c= NUL; break;	/* on */

					case SO: page_pause= 1; c= NUL; break; /* off */
					case SI: page_pause= 0; c= NUL; break; /* on */

					case STX: kblock= 1; abort= 0; c= NUL; break;
					case ETX: kblock= 0; abort= 0; c= NUL; break;

					case LF: c= NUL; break; /* LFs ignored */
				}
				if (c) break;		/* got a real one or CR */
			}
			if (count == 0) break;		/* end of file, word too */

/* The CR/SOH machine detects "IFNA Kludge" route lines and other nonsense
embedded in messages; this detects lines beginning with ^A and does not 
output them. */

			if (skipline) {			/* if machine is on, */
				if (c == CR) skipline= 0; /* CR turns it off, */
				continue;		/* eat characters */
			}
			skipline= (c == SOH) && (lastc == CR);
			if (skipline) continue;		/* check for new start */

			if (c == CR) break;		/* end of (the) wor(l)d */

			word[i++]= c;			/* build the word */
			word[i]= NUL;			/* for output */
			if ((c == TAB) || (c == ' ')) break;
		}

/* Possibly suppress SEEN-BY lines if they occur at the start of the line */

		if (fbit(FBIT_SEENBY)) {		/* if suppressing SEEN-BYs */
			if ((fcomp(word,"SEEN-BY",7) == 0) && (screen == 0)) {
				skipline= 1;		/* dont output until */
				continue;		/* next CR */
			}
		}
		if (page_pause) line= 0;		/* disables "more" */

		if (!(margrel || word_wrap) && (wordlen(word,screen) + screen > width)) {
			mcrlf();			/* too long; wrap */
			screen= 0;			/* before typing word */
		}
		for (cp= word; *cp; ++cp) {
			if (*cp == SOH) mconout(*cp);	/* KLUDGE! echo as ^A */
			else mconoutr(*cp);		/* else raw text */
		}
		screen += wordlen(word,screen);		/* size on screen */
		if (c == CR) {				/* if a hard CR, */
			mcrlf();			/* do a CR LF */
			screen= 0;
		}
		if (count == 0) break;			/* check EOF */
	}
	mcrlf();
	kblock= 0;					/* unlock it */
}

/* Logoff command; either from the console or by exceeding the time limit. 
We are passed the answer to the question if not a caller command. */

void goodbye(p)
char *p;
{
int yn;
char work[SS];

	if (fido.logonbr != 255) {			/* if one defined, */
		if (getmarea(fido.logonbr)) {		/* load it, */
			if (*p) yn= (tolower(*p) == 'y');/* if a valid area */
			else yn= nverf(CM+268);		/* ask "lv a msg?" */
			if (yn) {
				mprintf(CM+8);		/* "wait..." */
				msgcount();
				dispbbs("goodbye1.bbs");/* instructions */
				msend(0,1);		/* get a message */
			}
		}
	}
	gtod(work);
	dispbbs("goodbye2.bbs");			/* dump  a parting shot */
	mprintf(CM+152,caller.name,work);

	logoff(0,1);
}

/* Return the length of the text, as displayed on the screen. This is
basically just its strlen(), but tabs are essentially variable width depending
on the starting column. */

int wordlen(s,col)
char *s;
int col;
{
int n;

	for (n= 0; *s;) {
		if (*s == TAB) {		/* if a tab, */
			do ++n; 		/* effective width on screen */
			while (++col % 8);	/* plus update column */

		} else ++n;			/* else a regular character */
		++s;				/* next ... */
	}
	return(n);
}
