#include "fido.h"
#include "ascii.h"
#include "proto.h"

/* Main Fido execution loop; watch for incoming calls and scheduled
events. */

#ifdef CPC
char banner[] = "Canada Post Corp. ";
#else
char banner[] = "";
#endif

extern char _spr_sepchar;		/* printf() global */

unsigned _stack = 3500;			/* for autos and stack, */
unsigned zmttype = 0;			/* Zmodem Tx behaviour */
unsigned zmrtype = 0;			/* Zmodem Rx behaviour */
unsigned zmblkmax = 8192;		/* Zmodem maximum block size */
unsigned zmblkst = 1024;		/* Zmodem start with this block size */
char *zmbuff;				/* Zmodem Rx/Tx buffer */

char *texttbl[4];			/* table of text table pointers */

char *fmtable[] = {			/* Fixed Message Table */
	"",							/* FM0 */
	"\377",							/* FM1 */
	""							/* end of table */
};

char *lmtable[] = {			/* Log Message Table */
	"",							/* LM0 */
	"",							/* LM1 */
	"",							/* LM2 */
	"! wrong/missing nodelist file(s)\377",			/* LM3 */
	"! Cant AutoLog recd #%d?\377",				/* LM4 */
	"! Creating new %s\377",				/* LM5 */
	"! Wrong pwd: %s\377",					/* LM6 */
	"----\377",						/* LM7 */
	"+ \"%s\" on at %s, ",					/* LM8 */
	"test mode\377",					/* LM9 */
	"%d baud\377",						/* LM10 */
	"! 24 hr. limit\377",					/* LM11 */
	"! Help file \"%s\" missing\377",			/* LM12 */
	"! no specific help in \"%s\" for '%c'\377",		/* LM13 */
	"! no file areas?\377",					/* LM14 */
	"! outside\377",					/* LM15 */
	"! Disk full writing file \"%s\"\377",			/* LM16 */
	"! DL %s %s\377",					/* LM17 */
	"= DL ",						/* LM18 */
	"* time limit\377",					/* LM19 */
	"* near time limit\377",				/* LM20 */
	"* K limit\377",					/* LM21 */
	" N.F.",						/* LM22 */
	" ABORTED",						/* LM23 */
	" ERROR",						/* LM24 */
	" %d OK\377",						/* LM25 */
	"! UL %s %s\377",					/* LM26 */
	"= UL ",						/* LM27 */
	"! Name changed to: %s\377",				/* LM28 */
	"! Page, NA\377",					/* LM29 */
	"* Inactive\377",					/* LM30 */
	"! Can't find file \"%s\"\377",				/* LM31 */
	"! edit msg %d in area %d\377",				/* LM32 */
	"\377+ %d times, %d min\377\377",			/* LM33 */
	"\377---- Unscheduled Incoming Mail at %s\377",		/* LM34 */
	"---- Complete at %s\377\377",				/* LM35 */
	"\377---- FidoNet \"%c\" begins at %s (%d%c)\377",	/* LM36 */
	"---- FidoNet \"%c\" complete at %s\377\377",		/* LM37 */
	"! no msg areas?\377",					/* LM38 */
	"! Can't create file \"%s\"\377",			/* LM39 */
	"---- Fido/FidoNet %d%c starts at %s\377",		/* LM40 */
	"---- Fido/FidoNet %d%c ends at %s\377",		/* LM41 */

	""

};

char dayname[8][4] = {
	"Sun",
	"Mon",
	"Tue",
	"Wed",
	"Thu",
	"Fri",
	"Sat",
	"All"
};

struct _node id;		/* our FidoNet ID */
struct _node altid;		/* our FidoNet alternate ID */

char lmtstate;			/* current logoff limit warning state */
char taskid;			/* current Fido taskID (MLINK, etc) */

FLAG ext_mail;			/* external mail set by ROUTER */
FLAG send_only;			/* send mail only set by ROUTER */
FLAG recv_only;			/* receive mail only set by ROUTER */
FLAG cm_only;			/* send only to #CM nodes set by ROUTER */
char dial_tries;		/* number of times to try calling */
char connect_tries;		/* ditto, but with connects */
char rings;			/* number of rings to answer the phone */
char mdmstr[40];		/* modem initialization string */
char dial_interval;		/* how often to dial */
char cont_interval;		/* how often to reenable CONT events */
char dial_pref[30];		/* FidoNet dial string prefix */

unsigned mdmtype;		/* type of modem */
unsigned maxbaud;		/* maximum baud rate */
unsigned linkrate;		/* modem fixed link rate else 0 */

struct _fido fido;		/* main system file */

struct _area msgarea;		/* current area */
struct _area filearea;
char ovpath[SS];		/* file area override pathname */

struct _clr caller;		/* caller record */
struct _msg msg;		/* message header */
struct _node msg_orig;		/* KLUDGE for backwards compatibility */
struct _node msg_dest;		/* KLUDGE for backwards compatibility */
struct _nmap nmap;		/* node routing controls */
struct _ndat ndat;		/* node physical info */
struct _ntc ntc;		/* Node To Call index */

int *highmsg;			/* table of highest msgs # in system, */
int *totlmsg;			/* table of total number of messages, */

int msgfile = -1;		/* global handle for opened message, (.MSG) */
int cmtfile = -1;		/* logon and comment file, (.LOG) */
int idxfile = -1;		/* nodelist index file (NODELIST.IDX) */
int ndxfile = -1;		/* nodelist net index file (NODELIST.NDX) */
int zdxfile = -1;		/* nodelist zone index file (NODELIST.ZDX) */
int nodefile = -1;		/* nodemap file (NODELIST.NMP) */
int ndatfile = -1;		/* nodelist file (NODELIST.BBS) */
int ntcfile = -1;		/* nodes to call file (NODELIST.NTC) */

char doscode;			/* DOS error code */
char mailcode;			/* errorlevel to use when msgs received */
char filecode;			/* errorlevel to use when files received */
char crashcode;			/* errorlevel to use when YOOHOO/TSYNC recv'd */
FLAG crashmail;			/* 1 == specially process YOOHOO/TSYNC if recv'd */

int column;			/* column number, */
int line;

FLAG kblock;			/* 1 == disable Control-C and -K */
FLAG abort;			/* 1 == Control-C typed during output */
FLAG wlimit;			/* 1 == warned of time left */
FLAG wover;			/* 1 == no final time limit message */

FLAG terminate;			/* 1 == terminate after this caller */
FLAG echo;			/* 1 == echo on line input, */
FLAG test;			/* 1 == test mode, (no modem) */
FLAG localout;			/* 1 == echo caller IO to local cosole */
FLAG localin;			/* 1 == simultaneous keyboards */

unsigned limit;			/* time limit in force, */
unsigned user_limit;		/* current user time limit */
unsigned klimit;		/* download limit in force */
unsigned dlimit;		/* daily time limit in force */
FLAG hlimit;			/* 1 == hard time limit */

char langpath[PS];		/* current language-file path */

char date[20];			/* caller signon time */
int pktnum;			/* incoming packet number */
FLAG cd_flag;			/* 1 == ignore CD line */
unsigned datarate;		/* baud rate to/from caller */
unsigned online_baud;		/* command line baud rate */
unsigned ioport;		/* serial I/O port */

unsigned totl_files;		/* total files sent/rec'd */
unsigned totl_errors;		/* error count, soft errors incl */
unsigned totl_process;		/* 1 == file is in process */
unsigned totl_recoveries;	/* number of error recoveries */
long totl_bytes;		/* total bytes sent */

unsigned filemode;		/* transfer type; XMODEM, MODEM7, TELINK */

char brkflg;			/* stored DOS BREAK enable */

char *mem;			/* start of available memory */
unsigned memsize;		/* how big it is */
char *text;			/* text work space (set at language-load time) */
unsigned textsize;		/* how big it is */

/*extern unsigned _top; */		/* FIDOC.ASM stack base and top */
extern char *_base;

FLAG reptflg;			/* debug mode crap */

main(argc,argv)
int argc;
char **argv;
{
char *p,c,buff[SS];
unsigned n;
int e,i;
FLAG rdymsg,evtmsg;
int mdm;			/* current modem state */

long cont_tmr;			/* timer to reset CONT events */
int h1,h2;			/* (timer handles) */

	fidoini(argc,argv);			/* one time startup */
	if (reptflg) printf(0," * _stack=%04x, _base=%04x\r\n",_stack,_base); 
	brkflg= _break(0,0);			/* get break, */
	_break(1,0);				/* turn it off */

	fido_settings();			/* set FIDO.INI settings */
	set_clk();				/* install clock */
	set_hw();				/* select/init COM port & modem */

/* Start up the hardware. If we have fido.assume_baud rate set, then assume that 
there is a caller logged on and ready to go at the right baud rate. This is
done only once, and assumes that a caller went O)utside Fido earlier, and is
now returning. If there is no carrier, forget the whole thing. */

	rdymsg= 0;				/* No "waiting ..." msg yet */
	evtmsg= 0;				/* no "Scheduled ..." msg yet */

	cont_tmr= 0L;				/* reset the clock, */
	h1= set_tick(&cont_tmr);		/* start them */
	clr_clk();

/* SPECIAL CASE: Handle Test Mode right now. Since it sets doscode, it will
not run the Big Loop. */

	if (test) {				/* (no logging in test mode) */
		open_log("fido.log");		/* start logging, */
		do_fido();			/* do Fido */
		close_log();
		doscode= 1;			/* stop running */

/* SPECIAL CASE: If not test mode, check for the autolog record number; log 
this caller in right now. This then falls into the Big Loop which checks
doscode (maybe set by G-ErrorLevel) and then processes more callers. */

	} else if (fido.autolog != -1) {
		cprintf(SM+135);
		open_log("fido.log");		/* start logging, */
		do_fido();			/* execute Fido now, */
		close_log();			/* doscode may be set now */
	}

/* Update existing packets; messages may have been added while in DOS (or
by a caller that logged off previously but terminated to DOS before
net_new() got it it.) This really only matters when we have KEEP-PACKETS
set.  */

	if (fbit(FBIT_KPACKET)) net_new();	/* update packets */

/* Start the session by filling up the log file. */

	open_log("fido.log");		/* start logging, */
	gtod(buff);
	lprintf(LM+40,FIDOVER,FILEVER+96,buff);
	close_log();

/* 
	*************** The Big Loop. **************
*/
	while (1) {				/* exit-to-DOS code */
		limit= 0;			/* no time limit here! */

/* If ^T (Terminate after this caller) was typed during the last call, wake 
up the sysop. If ^C is typed, return to DOS; if ^Z typed, skip this and 
return to normal operation. */

		if (terminate) {
			terminate= 0;		/* clear it */
			cprintf(SM+136);
			for (i= 5 * 60; i > 0; i--) {
				lconout(BEL);
				delay(100);
				switch (bdos(6,0xff)) {
					case ETX: i= 1; doscode= 1; break;
					case SUB: i= 0; rdymsg= 0; break;
				}
			}
		}

/* Check for local console commands. */

		c= bdos(6,0xff);
		switch (tolower(c)) {		/* poll keyboard, */
			case ETX:		/* Control-C */
				doscode= 1; break;

			case '1': case 'q':	/* quick login */
				if (fbit(FBIT_QUICK)) {
					fido.autolog= 0; /* login first caller */
				}
						/* fall through ... */
			case '2': case 'l':	/* Test Mode */
				test= 1;	/* test mode */
				open_log("fido.log");
				i= localout; localout= 1;
				do_fido();
				close_log();

				localout= i;	/* preserve this */
				net_new();	/* repacket as necessary */
				rdymsg= 0;	/* waiting ... message */
				test= 0;	/* not test mode */

/* Since the phone may have been ringing while we were on, flush out
any pending result codes. */

				cd_flag= 1;
				flush(0);	/* flush any built up garbage */
				break;

			case '3': case 'e':	/* list events */
				til_sched('?',-1,1); /* find an event right NOW */
				rdymsg= 0;
				break;

			case '4': case 'p':
				open_node();	/* files are closed now */
				open_nmap(fido.event); /* get specific nodemap */
				mail_pending();
				close_node();	/* and we're done */
				rdymsg= 0;
				break;

			case '5': case 'f':
				if (linkrate) sprintf(buff,0,"FT /! %d/B /%d /F",maxbaud,iodev + 1);
				else sprintf(buff,0,"FT /! %d/B /%d",maxbaud,iodev + 1);
				execute(buff);
				modem_init(0);	/* reinit modem */
				rdymsg= 0;
				break;

			case '6': case 'r':	/* clear last-run event marker */
				clrevts();	/* clear all events */
				break;

			case '7': case 'i':	/* init the modem */
				discon();	/* disconnect/clear first */
				if (modem_init(0)) rdymsg= 0;
				break;

			case '?':		/* list events coming up */
			case '/':
				gtod(buff); cputs(buff); cputs("\377");
				cprintf(SM+138);
				cprintf(SM+139);
				cprintf(SM+140);
				cprintf(SM+141);
				rept_stack();	/* stack usage report */
				cprintf(FM+1);
				rdymsg= 0;	/* message again afterwards */
				break;
		}

/* This is the only check for 'doscode'. */

		if (doscode) break;

		mdm= answer();				/* check modem state now */

		if (!online_baud) {			/* let in forced callers */

/* If an event is ready to run, go do it. Events are checked before 
incoming calls. The modem is not initialized yet. */

			e= til_sched('?',0,0);		/* find an event right NOW */
			if (e >= 0) {
				if (fido.sched[e].tag == 'X') {
					dsp_event(buff,e);	/* display it, */
					cprintf(SM+134);	/* " * " */
					cputs(buff);		/* the event */
					doscode= fido.sched[e].result;
					markevt(e);		/* flag it as run */
					break;			/* ...out of big loop */

				} else if (!(mdm && cont(e))) {	/* CONT FidoNet */
					open_log("fidonet.log");
					if (do_fidonet(e))	/* do it! */
						rdymsg= 0;	/* need a msg if something happened */
					close_log();
					cont_tmr= 0L;		/* defer reenabling CONT events */
					continue;		/* check for more events, etc */
				}

/* No new event to start; if there is one still current but inactive (ie.
"complete", ie. a RUSH event) leave it alone, otherwise end it. */

			} else if (fido.event != -1) {	/* event has ended */
				if (til_event(fido.event)) {
					end_fidonet();	/* stop any running sched */
					rdymsg= 0;	/* need a new message */
					continue;	/* in case immediate event */
				}
			}

/* No events pending; every 17 minutes, initialize the modem if it is idle. */

			if (! mdm) if (modem_init(17)) rdymsg= 0;
		}
		
/* OK, no events etc. If the phone rings, answer it. If online is set,
then assume we are connected and ready at the already set linkrate 
and datarate. If TSYNC is received during the signon process, it
drops into Incoming Mail. fido_ov_load() is a dummy to pre-load the Fido
overlay. */

		fido_ov_load();			/* in fido.c, */
		if (mdm > 0) {			/* if an incoming call, */
			open_log("fido.log");
			switch (i= do_fido()) {	/* see why it terminated */
				case YOOHOO:
				case TSYNC:	/* probably crash mail */
					close_log();
					open_log("fidonet.log");
					inmail(i);
					break;

				case 0:		/* External Login */
					break;	/* doscode is already set */
			}
			close_log();		/* caller/mail done */

			net_new();		/* repacket if necessary */
			rdymsg= 0;		/* need ready msg */
			continue;		/* check again */

		} else if (mdm == -2) {		/* just became idle */
			rdymsg= 0;		/* issue a new ready msg */

		} else if (mdm == 0) {		/* if currently idle */
			if ((cont_tmr / 60000L) >= cont_interval) {
				clrcevts();	/* clear all CONT events */
				cont_tmr= 0L;	/* to rerun them */
			}
		}

/* Nothing to do. Display a message saying we're bored. */

		if (!(rdymsg || mdm)) {
			cprintf(SM+143);
			rdymsg= 1;
		}

/* Since we had nothing else to do, pass extra time to any multitaskers. */

		idle();				/* what the hell */

/* If 'online_baud' is set, we were invoked from another program; if we haven't
already, exit from the loop and return to DOS, even if no error code was set. */

		if (online_baud) break;
	}

/* Check if a FidoNet event was running. Normally we end the session,
logging the results, etc, but if KEEP-PACKET is on, then we save packets
unless we had an error (result code < 3). */

	if (fido.event != -1) {
		e= fido.event;
		if (!fbit(FBIT_KPACKET) || (doscode < 3)) end_fidonet();
		clrevt(e);			/* make it restart later */
	}
	open_log("fido.log");			/* start logging, */
	gtod(buff);
	lprintf(LM+41,FIDOVER,FILEVER+96,buff);
	close_log();

	putsys();				/* any scheduler changes, etc */
	clr_tick(h1); 				/* release the timers */
	clr_tick(h2); 
	reset_hw();				/* close up stuff */
	reset_clk();				/* turn off clock, */
	_break(1,brkflg);			/* restore break */
	exit(doscode);				/* back to DOS */
}

/* Select and initialize the serial driver, the modem type, etc. */

void set_hw() {

	if (test) return;

	if (iodev != ioport) {			/* if about to change, */
		if (iodev != -1) uninit();	/* uninstall, if present */
		iodev= ioport;			/* set global iodev */
		init();				/* install driver */
	}
	modem_type();				/* set modem type/max baud */
}

/* Uninitialize hardware things and close logs, etc. */

void reset_hw() {

	if (test) return;

	uninit();				/* always ints off, etc */
}

/* Load modem and related variables from FIDO.SYS. */

void fido_settings() {

	rings= fido.rings;			/* number of rings to answer the phone */
	strcpy(mdmstr,fido.mdmstr);		/* modem initialization string */
	connect_tries= fido.connect_tries;	/* attempt with connect limit */
	dial_tries= fido.dial_tries;		/* dial attempts */
	dial_interval= fido.dial_interval;	/* how often to dial */
	cont_interval= fido.cont_interval;	/* how often to reenable CONT events */
	strcpy(dial_pref,fido.dial_pref);	/* FidoNet dial string prefix */

	mailcode= fido.mailcode;		/* default mail-errorlevel */
	filecode= fido.filecode;		/* default file-errorlevel */

	send_only= 0;
	recv_only= 0;
	ext_mail= 0;

	cd_bit= fido.cd_bit;			/* set modem CD mask bit */
	mdmtype= fido.mdmtype;			/* modem type, */
}

/* Determine and display stack usage. */

void rept_stack() {
char *cp;

	if (! reptflg) return;

	for (cp= _base; *cp == 0xae; cp++);	/* find lowest used byte */
	clprintf(0," * Peak stack depth = %,d bytes\377",_stack - (cp - _base));
}
