// --------------------------------------------------------------------------
// Citadel: CTDL.H
//
// #include file for all Citadel CPP files.

#define ALPHA		1
#define BETA		2
#define PUBLICTEST	3
#define RELEASE 	4

#define VERSION RELEASE

// Uncomment for multi-user version. (Well, what little there is of it.)
//#define MULTI 1

#if VERSION == ALPHA || (VERSION == PUBLICTEST && (defined(AUXMEM) || defined(WINCIT)))
	// Include assertions
#else
	#define NDEBUG
#endif

#ifndef EXTERN
#define EXTERN extern
#endif

// Don't stack check release code. If it dies, it dies.
// Also, turn off -x (C++ exceptions) and -RT (run-time type identification)
// if using Borland C++ 4.00
#ifdef __BORLANDC__
	#if VERSION == RELEASE
		#pragma option -N-
	#else
		#pragma option -N
	#endif

	#if __BORLANDC__ == 1106 || __BORLANDC__ == 1120
		#pragma option -x-
		#pragma option -RT-
	#endif
#endif

#ifdef WINCIT
#include <windows.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <ctype.h>
#include <direct.h>
#include <time.h>
#include <dos.h>
#include <conio.h>
#include <string.h>
#include <stdarg.h>
#include <bios.h>
#include <direct.h>
#include <io.h>
#include <share.h>
#ifndef WINCIT
#include <malloc.h>
#endif
#include <process.h>
#include <fcntl.h>
#include <signal.h>
#include <limits.h>
#include <sys\stat.h>
#include <errno.h>

#include "pragmas.h"

#ifdef __BORLANDC__
	// Modify's BC++'s Assert to save some DGROUP...

	#if !defined( __DEFS_H )
	#include <_defs.h>
	#endif

	extern "C" void _Cdecl _FARFUNC __assertfail( char _FAR *__msg,
			char _FAR *__cond, char _FAR *__file, int __line);

	#undef assert

	#ifdef NDEBUG
		#define assert(p)	((void)0)
	#else
		#ifdef MAIN
			#ifdef WINCIT
				char *AssertFail = "Error:%s %s:%d";
			#else
				char *AssertFail = "Error:%s %s:%d\n";
			#endif
		#else
			extern char *AssertFail;
		#endif

		#define assert(p) ((p) ? (void)0 : (void) __assertfail( \
						AssertFail, "", __FILE__, __LINE__))
	#endif
#else
	#include <assert.h>
#endif

#undef toupper
#undef tolower
#undef strcmpi
#undef strcmp

// Miscelaneous #defines
#define TRUE			1
#define FALSE			0
#define CERROR			-1
#define ULONG_ERROR 	0xFFFFFFFFL
#define UINT_ERROR		0xFFFF

#define SAMESTRING		0		// value for strcmp() & friend
#define SameString(a,b) (strcmpi((a), (b)) == SAMESTRING)

#define SCROLL_SAVE 	1		// for cls()
#define SCROLL_NOSAVE	0

#define CTRL_A	(1)
#define CTRL_B	(2)
#define BELL	(7)
#define LF		(10)
#define CR		(13)
#define ESC 	(27)

typedef int (cdecl *QSORT_CMP_FNP)(const void *, const void *);


// #defines to override standard library functions
#define strcmp(a, b)	deansi_str_cmp(a, b)
#define stricmp(a, b)	deansi_str_cmpi(a, b)
#define strcmpi(a, b)	deansi_str_cmpi(a, b)
#define strftime		strcitftime
#define fopen			CitFopen
#define unlink			CitUnlink
#define rename			CitRename
#define strdup			CitStrdup

#undef memcpy
#undef strcpy
#undef memset

#ifndef NDEBUG

#pragma intrinsic memcpy
#pragma intrinsic strcpy
#pragma intrinsic memset

	// debugging overrides
	inline void *CITmemcpy(void *dest, const void *src, size_t len)
		{
		assert(dest != NULL);
		assert(src != NULL);
		assert(len != 0);

		return (memcpy(dest, src, len));
		}

	inline char *CITstrcpy(char *dest, const char *src)
		{
		assert(dest != NULL);
		assert(src != NULL);

		return (strcpy(dest, src));
		}

	inline void *CITmemset(void *dest, int what, size_t len)
		{
		assert(dest != NULL);
		assert(len != 0);

		return (memset(dest, what, len));
		}

#pragma intrinsic -memcpy
#pragma intrinsic -strcpy
#pragma intrinsic -memset

	#define memcpy(x,y,z)	CITmemcpy(x, y, z)
	#define strcpy(x,y) 	CITstrcpy(x, y)
	#define memset(x,y,z)	CITmemset(x, y, z)
#else
	// standard overrides
	#define memcpy(x,y,z)	_fmemcpy(x, y, z)
	#define strcpy(x,y) 	_fstrcpy(x, y)
	#define memset(x,y,z)	_fmemset(x, y, z)
#endif

// copy a string into a buffer of a known length
inline void CopyStringToBuffer(char *dest, const char *src, int Length)
	{
	assert(dest != NULL);
	assert(src != NULL);

	strncpy(dest, src, Length);
	dest[Length] = 0;
	}

#ifdef MSC
#define time(a) CitadelTime(a)
#endif

// The following are easier to type and make it easier to format code.
typedef unsigned long	ulong;
typedef unsigned short	ushort;
typedef unsigned int	uint;
typedef unsigned char	uchar;
typedef uint			Bool;
typedef void *			CITHANDLE;


#ifdef AUXMEM
	#define AUXMEMCODE(x)	x
#else
	#define AUXMEMCODE(x)
#endif

#if defined(AUXMEM) || defined(WINCIT)
	#define AUXMEMWINCODE(x)	x
#else
	#define AUXMEMWINCODE(x)
#endif

#ifdef NDEBUG
	#define DEBUGCODE(x)
#else
	#define DEBUGCODE(x)	x
#endif


// This lets the Windows version keep files closed that the DOS version
// always leaves open for faster access.
#ifdef WINCIT
struct citFILE
	{
	int fileHandle;
	int citType;
	long pos;
	OFSTRUCT of;
	};
#else
typedef FILE * citFILE;
#endif

// for citOpen stuff
enum CitOpenCodes
	{
	CO_A = 1,	CO_AB,	CO_AP,	CO_R,	CO_W,	CO_WB,
	CO_RB,		CO_RPB, CO_WPB,
	};


// These define the sizes of some Citadel things.
typedef int 	r_slot; 			// what a room is
typedef int 	h_slot; 			// what a hall is
typedef int 	l_slot; 			// what a log table entry is
typedef int 	l_index;			// what a log file entry is
typedef int 	g_slot; 			// what a group is

#if defined(AUXMEM)
	typedef ulong			m_slot;
	#define M_SLOT_ERROR	ULONG_MAX
#elif defined(WINCIT)
	typedef ulong			m_slot;
	#define M_SLOT_ERROR	ULONG_MAX
#else
	typedef int 			m_slot;
	#define M_SLOT_ERROR	-1
#endif

#define LABELSIZE		30			// length of room names
#define BORDERSIZE		80			// length of borders.
#define PATHSIZE		256 		// length of msg paths
#define MAXTEXT 		8192		// cheating (message buffer size)
#define MAXDIRS 		6			// number of directions
#define MAXWORD 		256 		// maximum length of a word
#define DOSPATHSIZE 	63			// wow.
#define ANSILEN 		300 		// Max length of ANSI/Music string
#define BOOL_COMPLEX	25			// how complex we can make them
#define KEYBUFSIZE		101 		// How big our keyboard buffer is + 1.

typedef char			label[LABELSIZE+1];
typedef char			fpath[DOSPATHSIZE+1];
typedef uint			BoolExpr[BOOL_COMPLEX + 1];


enum PortSpeedE
	{
	PS_300, 		PS_600, 		PS_1200,		PS_2400,
	PS_4800,		PS_9600,		PS_19200,		PS_38400,
	PS_57600,		PS_115200,

	PS_NUM, 		PS_ERROR
	};

enum ModemSpeedE
	{
	MS_300, 		MS_600, 		MS_1200,		MS_2400,
	MS_4800,		MS_7200,		MS_9600,		MS_12000,
	MS_14400,		MS_16800,		MS_19200,		MS_21600,
	MS_24000,		MS_26400,		MS_28800,		MS_38400,
	MS_57600,		MS_115200,		MS_230400,

	MS_NUM, 		MS_ERROR
	};

// it's amazing how often you need this. well, perhaps not "amazing."
#define SECSINDAY	86400l

#include "keywords.h"

// This next part defines the structures of the data files and tables
// used to index them.

/* -------------------------------------------------------------------- */
/*																		*/
/*				!	!  !!!	 !!!!!	!!!!!	!!							*/
/*				!!	! !   !    !	!		!!							*/
/*				! ! ! !   !    !	!!! 	!!							*/
/*				!  !! !   !    !	!									*/
/*				!	!  !!!	   !	!!!!!	!!							*/
/*																		*/
/* Citadel uses readTables() and writeTables() to write images			*/
/* of the external variables in RAM to disk, and later restore			*/
/* them.  The images are stored in Etc.tab and all the .TAB files.		*/
/* Ctdl.Exe will automatically reconstruct these files the hard way 	*/
/* when necessary and write them out when finished. 					*/
/* Etc.tab and the .TAB files are always destroyed after reading, to	*/
/* minimize the possibility of reading out-of-date versions.  In		*/
/* general, this technique works well and saves time and head-banging	*/
/* upon bootup.  You should, however, note carefully the following		*/
/* caution: 															*/
/*	o  Whenever you change the declarations of any tables you should:	*/
/*	 -->  Destroy the current Etc.tab file. 							*/
/*	 -->  Recompile and reload Ctdl.Exe.								*/
/*	 -->  Ctdl.Exe will automatically build new Etc.tab and tables. 	*/
/*																		*/
/* If you ignore this warning, little pixies will prick you in your 	*/
/* sleep for the rest of your life. 									*/
/* -------------------------------------------------------------------- */

// This first structure is the is the contents of ETC.TAB.
// It contains configuration information.
struct config
	{
	long	version;

	ulong	callno; 			// how many calls the system has gotten
	ulong	oldest; 			// 32-bit ID# of first message in system
	ulong	newest; 			// 32-bit ID# of last message in system
	ulong	mtoldest;			// oldest message ID # in message table
	long	catLoc; 			// where to begin writing next message
	ulong	newestbuildaddress; // for buildaddress()

// -------------- System name, region, and other net stuff ------------
	label	nodeTitle;			// name of the system
	label	nodeRegion; 		// name of the system's region
	label	nodeCountry;		// name of the system's country
	label	Cit86Country;		// For Citadel-86 style netting
	label	Cit86Domain;		// For Citadel-86 style netting
	label	softverb;			// this is really soft adjetive.
	char	nodeSignature[91];	// Signature line for the node
	char	PAD;
	long	sig_first_pos;		// Position of first signature (Multi)
	long	sig_current_pos;	// Current position of signature (Multi)
	char	alias[4];			// alias, for routing
	char	locID[5];			// location identifier, for routing
	char	Address[9]; 		// alias.region together
	label	nodephone;			// phone #, in "(206) 555-1212" format

	label	twitRegion; 		// how many stupid fields did we get
	label	twitCountry;		// from jj and zen master?

	label	sysop;				// name of the system operator

	Bool	forward;			// forward sysop&aide messages to sysop?
	Bool	showSysop;			// show .AL stuff

// ------------------------- Modem Stuff ------------------------------
	int 	mdata;				// 3f8 com 1, 2f8 com 2
	int 	dumbmodem;			// See CONFIG.CIT
	PortSpeedE initbaud;		// Baud to start modem at
	ModemSpeedE minbaud;		// minimum allowed baud
	char	modsetup[64];		// Normal init string
	char	modunsetup[64]; 	// Normal de-init string
	label	downshift;			// downshift string
	char	dialsetup[64];		// Dial out string
	label	dialpref;			// ATDT
	label	dialring;			// What to dial when RING received
	int 	dialringwait;		// Time 'tween last ring and dialring
	char	updays[7];			// Which Days board is answering?
	char	uphours[24];		// Which hours board is answering?
	int 	hangupdelay;		// For Ogre's brain-dead modem.
	label	hangup; 			// how to hang up with #dumbmodem 6
	label	offhookstr; 		// how to offhook()
	int 	baudPause;			// For Emily's brain-dead modem.

// ---------------------------- Paths ---------------------------------
	fpath	homepath;			// which path system files will be in
	fpath	msgpath;			// which path message file will be in
	fpath	helppath;			// path which help files live in
	fpath	helppath2;			// 2nd path which help files live in
	fpath	temppath;			// temporary files (networking) go here
	fpath	roompath;			// where to look for room descriptions
	fpath	trapfile;			// where trap file goes
	fpath	aplpath;			// which path applications will be in
	fpath	dirpath;			// default path for directory rooms
	fpath	transpath;			// path transient files will be in
	fpath	printer;			// where to put output at alt-p
	fpath	dlpath; 			// where to put network file transfers
	fpath	logextdir;			// where to put log extension files
	fpath	lexpath;			// where to find dictionaries
	fpath	rlmpath;			// where to find RLMs
	fpath	ScriptPath; 		// Where to find CSFs

	uint	filebufsize;		// how big each file buffer is

// ----------------------- Default Terminal ---------------------------
	int 	autoansi;			// how to autoansi detect...

// ------------------------- New User ---------------------------------
	label	enter_name; 		// enter "you name" or "nym" or "..."
	label	newuserapp; 		// application to run for new users
	int 	oldcount;			// how many message new to new users
	Bool	l_closedsys;		// System is closed to new users
	Bool	l_verified; 		// Leave users verified
	Bool	l_sysop_msg;		// Message to Sysop
	Bool	l_create;			// create accounts

	NewUserQuestions NUQuest[NUQ_NUMQUEST+1];	// The questions to ask

// ------------------------- Accounting -------------------------------
	int 	unlogtimeout;		// how many idle mins unlogged user gets
	int 	timeout;			// how many idle mins logged user gets
	int 	consoletimeout; 	// how many idle mins console user gets
	int 	OutputTimeout;		// how many idle mins user gets when Citadel
								// is displaying information
	long	unlogbal;			// # credits unlogged users get
	int 	pwDays; 			// Days users are allowed to keep in;pw
	int 	callLimit;			// Number of calls allowed per day

// ------------------------- Appearance -------------------------------
	char	datestamp[64];		// default time date format string
	char	vdatestamp[64]; 	// default time date format string
	char	prompt[64]; 		// default prompt format string
	label	Umsg_nym;			// Upper-case name of message
	label	Umsgs_nym;			// Upper-case name of messages
	label	Uroom_nym;			// Upper-case name of room
	label	Urooms_nym; 		// Upper-case name of rooms
	label	Uhall_nym;			// Upper-case name of hall
	label	Uhalls_nym; 		// Upper-case name of halls
	label	Uuser_nym;			// Upper-case name of user
	label	Uusers_nym; 		// Upper-case name of users
	label	Ugroup_nym; 		// Upper-case name of group
	label	Ugroups_nym;		// Upper-case name of groups
	label	Ucredit_nym;		// Upper-case name of credit
	label	Ucredits_nym;		// Upper-case name of credits
	label	Umsg_done;			// Upper-case what to do to message
	label	Lmsg_nym;			// Lower-case name of message
	label	Lmsgs_nym;			// Lower-case name of messages
	label	Lroom_nym;			// Lower-case name of room
	label	Lrooms_nym; 		// Lower-case name of rooms
	label	Lhall_nym;			// Lower-case name of hall
	label	Lhalls_nym; 		// Lower-case name of halls
	label	Luser_nym;			// Lower-case name of user
	label	Lusers_nym; 		// Lower-case name of users
	label	Lgroup_nym; 		// Lower-case name of group
	label	Lgroups_nym;		// Lower-case name of groups
	label	Lcredit_nym;		// Lower-case name of credit
	label	Lcredits_nym;		// Lower-case name of credits
	label	Lmsg_done;			// Lower-case what to do to message
	label	netPrefix;			// default net prefix
	int 	nopwecho;			// what to echo initials & pw at login
	label	anonauthor; 		// author of anonymous messages
	label	twirly; 			// twirly string
	int 	twirlypause;		// twirly speed
	Bool	twitrev;			// Twitty implementation of Reverse?
	char	AltF3Msg[81];		// What to say, dude!
	char	AltF3Timeout[81];	// What to say, dude!

// ------------------------- System Size ------------------------------
	long	messagek;			// how many K message table is
	int 	maxfiles;			// Max number of files per directory
	int 	MAXLOGTAB;			// number of log entries supported
	int 	maxrooms;			// number of rooms
	int 	maxhalls;			// number of halls
	int 	maxgroups;			// number of groups
	int 	maxborders; 		// number of borders
	m_slot	nmessages;			// # of messages to be stored in table

// -------------------------- Features --------------------------------
	uint	borderfreq; 		// frequency of borders
	Bool	msgNym; 			// aides can change Message nyms
	Bool	borders;			// Borderlines enabled
	Bool	forcelogin; 		// Automatically log someone in?
	int 	offhook;			// TRUE to go off-hook when 'L' is hit
	Bool	accounting; 		// is accounting enabled on the system?
	Bool	netmail;			// put networked mail in proper rooms?
	int 	subhubs;			// provision for special g)oto looping
	Bool	colors; 			// allow colored room names, etc.
	char	moreprompt[80]; 	// configurable <more> prompt
	char	sleepprompt[80];	// configurable sleeping... prompt
	Bool	showmoved;			// show original room if not verbose
	Bool	censor; 			// Let users be able to view censored?
	Bool	countbeep;			// beep when counting down
	int 	sleepcount; 		// countdown timer for timout
	char	dialmacro[48][80];	// dialout macros - 48 should be
								//	DM_NUMKEYS, but it cannot...
	long	diskfree;			// nothing, really
	Bool	aideChatHours[24];	// when .AC works
	int 	chatmail;			// Leave msg to sysop when chatting?
	Bool	chatwhy;			// Reason for chatting.
	int 	chatflash;			// Flash border while ringing sysop.
	Bool	ecTwirly;			// if .EC can set twirly cursor
	Bool	ecUserlog;			// if .EC can set list in userlog
	Bool	ecSignature;		// if .EC can set user signature
	Bool	ecColor;			// if .EC can set color mode
	char	dictionary[13]; 	// the default dictionary in use
	Bool	ovrEms; 			// use EMS for overlays
	Bool	ovrExt; 			// use extended for overlays
	int 	checkSysMail;		// 0; no; 1 'sysop'; 2 #sysop; 3 both
	long	memfree;			// supershell if less
	Bool	music;				// esc[MZ/F sounds?
	int 	allowCrypt; 		// 0: None; 1: Mail; 2: Everywhere
	int 	badPwPause; 		// how long
	int 	altF3Time;			// How much time to give (Seconds).
	Bool	ignore_uptime;		// if Alt-U has been hit
	Bool	FastLogin;			// Log in quickly? (Auxmem/Windows)
	Bool	SaveJB; 			// Save #JB in LE*.DAT?
	Bool	SwapNote;			// Note supershell to user.
	Bool	FastScripts;		// Keep stuff loaded.
	Bool	NewNodeAlert;		// Alert in when added to ROUTE.CIT?
	Bool	Psycho; 			// Psycho filter enabled
	Bool	Backout;			// Backout filter enabled
	Bool	Mmmm;				// Mmmm filter enabled
	Bool	VerboseConsole; 	// Tell console user what he did?
	Bool	AltXPrompt; 		// Ask Yes/No?

// ------------------------- Privileges -------------------------------
	Bool	entersur;			// Let users enter their own surname?
	Bool	aidehall;			// Aides mess with halls.
	Bool	readluser;			// Read Limited Access Userlog?
	Bool	unlogEnterOk;		// TRUE if OK to enter messages anon
	Bool	unlogReadOk;		// TRUE if unlogged folks can read mess
	Bool	nonAideRoomOk;		// TRUE general folks can make rooms
	Bool	moderate;			// can aides see moderated messages?
	Bool	sysopOk;			// can make sysops from remote?
	uint	numRooms;			// how many rooms can be made per call?
	int 	readLog;			// Who can .RU?
	Bool	PersonalHallOK; 	// Can users use it?

	// TITLES / SURNAMES
	Bool	surnames;			// are the surenames on?
	Bool	netsurname; 		// Display networked surnames?
	Bool	titles; 			// Titles are on
	Bool	nettitles;			// Titles enabled

	// CONSOLE
	Bool	bios;				// to use BIOS calls for screen I/O?
	uchar	attr;				// color of text displayed on screen
	uchar	wattr;				// color of text displayed on window
	uchar	cattr;				// color of text displayed on window
	uchar	battr;				// color of text screen border lines
	uchar	uttr;				// color of text displayed in underline
	int 	screensave; 		// # minutes before blanking screen
	Bool	fucking_stupid; 	// whether or not to unblank the screen
								// upon carrier detect
	Bool really_fucking_stupid; // whether keep the screen unblanked
								// for the duration of the call
	Bool really_really_fucking_stupid;	// whether or not to keep the
										// cursor on while the screen
										// is blanked
	Bool	LockOnBlank;		// Lock console when saver turned on?
	int 	turboClock; 		// how often to move the turboClock
	label	SaverMsg;			// Replace turboClock?
	Bool	NoConsoleTrap;		// Don't trap stuff onConsole

	// OTHER STUFF
	int 	connectwait;		// how long to wait after we connect
	int 	idle;				// how long we wait before we net

	int 	ad_chance;			// chance 0-100% of an ad appearing
	uint	ad_time;			// time before random ad appears

	int 	ansiBye;			// Goodbye blbs.
	int 	normBye;			// Goodbye blbs.
	int 	ansiHello;
	int 	normHello;

	int 	ansiAd;
	int 	normAd;

	int 	ansiCometoChat;
	int 	normCometoChat;

	int 	ansiJoke;
	int 	normJoke;

	label	f6pass; 			// Console lock password
	Bool	FullConLock;		// If the console is fully locked
	char	trapit[18]; 		// which events are logged
	int 	MessageRoom;		// max messages per room per call
	Bool	noChat; 			// TRUE to suppress chat attempts
	int 	noBells;			// 2 to supress all bells, 1 all but chat
	Bool	mci;				// fuck this shit
	label	mci_name;			// if off, what to show...
	label	mci_firstname;
	label	mci_time;
	label	mci_date;
	label	mci_poop;

#define MAXCHT 30
	int 	LocalChat[MAXCHT];	// codes for local chat sound
	int 	LocalChatLen;		// How many are in use.

#ifdef AUXMEM
	char	vmemfile[64];		// where to make the virtual memory
#endif
	Bool	checkCTS;			// pay attention to CTS from the modem?

	int 	readall;
	int 	poop;
	Bool	poopdebug;
	long	maxpoop;
	label	poopuser;

	Bool	printerprompt;		// prompt for file when doing alt-p?

	long	scrollSize;
	Bool	scrollColors;
	uint	scrollTimeOut;
	Bool	msgCompress;
	Bool	restore_mode;
	int 	expire;
	int 	maxjumpback;
	uchar	fuelbarempty;
	uchar	fuelbarfull;
	int 	statnum;
	int 	wysiwyg;			// richard's silly little screen stuff
	int 	concolors;			// console color stuff
	Bool	forcetermcap;		// console termcap always on?

	Bool	speechOn;			// getting geeky with the sound blaster
	int 	maxerror;			// maximum # of errors before hanging up
	};

EXTERN config		cfg;		// A buncha variables

// so we can have a pointer to them in taskInfo. see various .H files.
class LogEntry;
class LogEntry2;
class Message;

// That is all we have in the way of data files. But, we have plenty
// of data structures in memory yet to define...

class AccountInfo;				// Stuff read from GRPDATA.CIT
class UserAccountInfo;			// Stuff for logged in user
class NodeCitInfo;				// Stuff read from NODES.CIT

enum MonthsE
	{
	M_JAN,	M_FEB,	M_MAR,	M_APR,	M_MAY,	M_JUN,
	M_JUL,	M_AUG,	M_SEP,	M_OCT,	M_NOV,	M_DEC,

	NUM_MONTHS
	};

enum DaysE
	{
	D_SUN,	D_MON,	D_TUE,	D_WED,	D_THU,	D_FRI,	D_SAT,

	NUM_DAYS
	};

// Citadel only has Xmodem (and 1K Xmodem and Xmodem CRC) built into
// it. For any serious file transfering (including those used for
// networking), you must tell it to use an external file transfer
// protocol. These are defined in EXTERNAL.CIT, and are read into the
// following structure, which forms a linked list.
struct protocols
	{
	protocols *next;
	label name; 				// The name of the protocol.
	label MenuName;
	char CommandKey;
	label autoDown; 			// An auto-download initiation string.
	char respDown[128]; 		// Command send with response file.
	char rcv[128];				// Command line to receive files.
	char snd[128];				// Command line to send files.
	int block;					// Size of block. (0 for variable.)
	Bool batch; 				// If it is a batch protocol.
	Bool NetOnly;				// Only for networking.
	int CheckType;				// Woo.
	char CheckMethod[128];		// Woo woo.
	};


// Citadel can be told to run any specific application when any
// specific user logs in. These "user applications" are defined in
// EXTERNAL.CIT and read into this structure, which forms a linked
// list.
struct userapps
	{
	userapps *next;
	label name; 				// The name of the user.
	char outstr[64];			// Anything to display to the user.
	label cmd;					// The command to run.
	Bool hangup;				// Hang up when done?
	};


// Citadel can be set up to use doors, which can also override the
// internal commands. Doors are defined in EXTERNAL.CIT and read into
// the following structure, which forms a linked list.
struct doors
	{
	doors *next;
	label name; 				// The name of the door.
	char cmd[41];				// The command to run.
	label group;				// Limited to a specific group...
	Bool DIR	: 1;			// Only in directory rooms.
	Bool SYSOP	: 1;			// Must be a Sysop to use it.
	Bool AIDE	: 1;			// Must be an Aide to use it.
	Bool CON	: 1;			// Must be on console to use it.
	int WHERE	: 2;			// 0=.ED_; 1=_,.ED_; 2=._,.ED_
	};


// Citadel can replace its standard date string with a holiday if the
// holiday has been specified. Specify holidays in EXTERNAL.CIT, and
// they get read into this structure, which forms a linked list.
struct holidays
	{
	holidays *next;
	label name; 				// The name of the holiday.
	MonthsE Month;				// The month of the holiday.
	int date;					// The date of the holiday.
	int week;					// The week of the month of the holiday.
	int year;					// The year of the holiday. (-1 for all)
	DaysE day;					// The day of the week of the holiday.
	};


// You can decide to censor messages based their content, author, or
// (in the case of networked messages), their originating node. This
// is defined in EXTERNAL.CIT, and the data is read into the following
// structure, which forms a linked list.
enum CensorType
	{
	CENSOR_TEXT,	CENSOR_AUTHOR,	CENSOR_NODE,
	};

struct censor
	{
	censor *next;
	CensorType what;			// What type of thing we are censoring.
	label str;					// And what exactly is it.
	};


// Another form of censorship, or just pure obnoxiousness (which is
// why it was implemented, really), is available by telling Citadel
// to replace any given word with another word or phrase. This is
// defined in EXTERNAL.CIT, and read into the following strucutre,
// which (amazingly enough) forms a linked list.
//typedef struct r
//	{
//	struct r *next;
//	label oldword;				// Replace this
//	label newword;				// With this.
//	} replace;



// Citadel allows Aides to create lists of messages, to operate on all
// at once. (Such as to kill them all at once, or insert all of them
// into another room.) This list is implemented with the following
// linked list. (Should it be done with an array? Perhaps. But it is
// not.)
struct messageList
	{
	messageList *next;
	long msg;					// ID of the message in the list.
	};


// Citadel keeps a stack of rooms that a user has been in. This is to
// allow them to "jump back" to rooms that they have left. This stack
// is implemented as an array of the following data.
struct jumpback
	{
	ulong	newpointer; 		// Which messages were new in the room.
	m_slot	newMsgs;			// How many messages were new.
	h_slot	hall;				// What hall was the user in.
	r_slot	room;				// Which room, too.
	Bool	bypass; 			// Had the room been bypassed.
	};


// Citadel's terminal support is currently very limited. However, the
// following data structure is in place and should be easy to expand
// to other terminal types.
struct terminal
	{
	label	normal; 			// Code to make the terminal normal.
	label	bold;				// Code to make the terminal bold.
	label	inverse;			// Code to make the terminal inverse.
	label	blink;				// Code to make the terminal blink.
	label	under;				// Code to make the terminal underline.
	Bool	ibmAnsi;			// Actually, IBM 128+ characters.
	Bool	ibmColor;			// Support ^Aa-h and A-H codes.
	};


//	Sometimes, it's useful to store a couple of strings in a pair.
//
//	One particularly frivilous feature of Citadel is the ability to
//	have special tags that are appended to the name of a user when
//	reading messages from that user. This is how it is done.
struct pairedStrings
	{
	pairedStrings *next;
	label string1;					// Which user is being tagged.
	label string2;				// And the tag is.
	};


// Citadel often keeps a list of strings in memory. (For example, the
// personal dictionary feature of the spell checker is implemented
// as one.) The following structure is used to define the list. Note
// that there is only space in the structure for one character of the
// string. This is to make efficient use of memory: we always know how
// long the string is going to be before we add it to the list. We use
// this knowledge to allocate just enough memory to contain the string
// and no more. The space to allocate for the string is given by the
// formula
//
//					sizeof(strList) + strlen(str)
//
// Where str is the string that we want to add to the list. strlen()
// returns the number of bytes in the string, not including the space
// required for the terminal Nul character. This is already in the
// structure, however, so we get just how much memory we want. It is
// then merely a matter of strcpy()ing the string to the new space.

struct strList
	{
	strList *next;
	char string[1]; 			// The string being stored.
	};

// For the F4 screen, and some internal processing, Citadel uses a
// global variable telling it what is going on. This variable can be
// assigned one of the following activities.
enum dowhattype
	{
	DUNO,					MAINMENU,				SYSOPMENU,
	PROMPT, 				MESSAGETEXT,			DIALOUT,
	NETWORKING, 			ENTERBORDER,			READMESSAGE,
	HELPFILES,				READUSERLOG,			READSTATUS,
	READCONFIG, 			READINTRO,				DODOWNLOAD,
	DOUPLOAD,				DOCHAT, 				DOENTER,
	DOVOLKS,				DOEXCLUDE,				DOGOTO,
	DOKNOWN,				DOLOGOUT,				DOREAD,
	DOSMALLCHAT,			DOAIDE, 				ENTERCONFIG,
	ENTERDOOR,				ENTERHALL,				ENTERPW,
	ENTERROOM,				ENTERTEXT,				ENTERWC,
	ENTERAPP,				ENTERNAME,				ENTERSUR,
	ENTERMENU,				DOBYPASS,				KNOWNROOMS,
	KNOWNHALLS, 			KNOWNRMINFO,			KNOWNMENU,
	TERMMENU,				TERMQUIT,				TERMSTAY,
	READDIR,				READINFO,				READHALLS,
	READTEXT,				READWC, 				READZIP,
	READMENU,				AIDEATTR,				AIDECHAT,
	EDITROOM,				AIDEFILESET,			AIDEGROUP,
	AIDEHALL,				AIDEINSERT, 			AIDEKILL,
	AIDELIST,				AIDEMOVE,				AIDENYM,
	AIDEQUEUE,				AIDEQUEUEM, 			AIDEQUEUEC,
	AIDEQUEUEI, 			AIDEQUEUEK, 			AIDEQUEUEL,
	AIDEQUEUER, 			AIDEQUEUES, 			AIDERENAME,
	AIDESETINFO,			AIDEUNLINK, 			AIDEVIEW,
	AIDEWINDOW, 			MOVEROOM,				AIDEMSGIN,
	AIDEMENU,				SYSMAINT,				SYSCRON,
	SYSOFFHK,				SYSDATE,				SYSENTER,
	SYSGRP, 				SYSHALL,				SYSINFO,
	SYSJOURNAL, 			SYSKUSER,				SYSMASS,
	SYSNUSER,				SYSSCR, 				SYSSHOW,
	SYSEDIT,				SYSVIEW,				SYSEXIT,
	SYSKEMPTY,				SYSSHELL,				SYSNET69,
	SYSAUXDEB,				SYSREADMSG,
	SYSCFG, 				SYSMENU,				S69FETCH,
	S69INC, 				S69ROOMREQ, 			S69MENU,
	SYSFPDOWN,				SECURITYVIOLATION,		LOGIN,
	SCROLLBACK, 			CONSYSOP,				FILEQUEUEL,
	FILEQUEUER, 			FILEQUEUEC, 			FILEQUEUEA,
	DOGOTOMAIL, 			DOPERSONAL, 			PERSONALMENU,
	PERSONALADD,			PERSONALRMV,			PERSONALLST,
	PERSONALUSE,			PERSONALADDALL, 		PERSONALADDLOCAL,
	PERSONALADDROOM,		PERSONALADDMENU,		PERSONALCLEAR,
	S86FETCH,				S86INC, 				FINGER,
	FINGERVIEW, 			FINGEREDIT, 			FINGERMENU,
	SYSTAB, 				FINGERLIST,
	};

// TERMCAP definitions
enum LogAttr
	{
	ATTR_NORMAL,			ATTR_BLINK, 			ATTR_REVERSE,
	ATTR_BOLD,				ATTR_UNDERLINE,

	ATTR_NUM,
	};

// list stuff
#define 	LIST_START	NULL
#define 	LIST_END	((char *) 1)


// modem stuff
enum ModemConsoleE
	{
	MODEM,		CONSOLE,	NOMODCON,
	};

enum EchoType
	{
	NEITHER,				// don't echo input at all
	CALLER, 				// echo to caller only -- passwords, etc.
	BOTH,					// echo to caller and console both
	};

#define NO_ECHO 	0		// Don't echo input (getString())
#define ECHO		1		// Echo input (getString())


// These flags are for special action during message retrvial
enum SpecialMessageOperations
	{
	NO_SPECIAL, 			// do not do anything special
	PULL_IT,				// kill the message
	MARK_IT,				// mark the message to be moved
	REVERSE_READ,			// change read direction
	COPY_IT,				// copy message to file (UNUSED)
	CENSOR_IT,				// censor toggle
	REPLY,					// Reply to it.
	};

// For reading by various criteria...
struct ReadFilter
	{
	Bool	Mail;
	Bool	Group;
	Bool	Public;
	Bool	Local;
	Bool	User;
	Bool	Text;

	label	SearchUser;
	label	SearchText;
	BoolExpr	SearchGroup;
	};

// The read options
struct MsgReadOptions
	{
	Bool	Number; 		// .R#...
	Bool	Headerscan; 	// .R!...
	Bool	Date;			// .R&...
	Bool	DatePrevious;	// .R&...
	Bool	All;			// .RA...
	Bool	Verbose;		// .RV...
	Bool	Reverse;		// .RR...

	time_t	CheckDate;

	long	MessageNumber;

	SpecialMessageOperations DotoMessage;
	};

// values for showMess and readdirectory/infofile/filldirectory routines
enum OldNewPick
	{
	NewOnly,		OldAndNew,		OldOnly,		WhoKnows,
	};

enum UserControlType
	{
	OUTOK,					// normal output
	OUTNEXT,				// quit this message, get the next
	OUTSKIP,				// stop current process
	OUTPARAGRAPH,			// skip to next paragraph
	IMPERVIOUS, 			// make current output unstoppable
	NOSTOP, 				// can pause, but not stop
	};

extern void setio(void);	// this sucks

class UserOutputControl
	{
	Bool CanK, CanM, CanStar;	// K, M, * allowed
	Bool CanR, CanBang, CanAt;	// R, !, @ allowed

	UserControlType OutFlag;	// OUTOK, IMPERVIOUS, etc.
	Bool			Continuous; // User hit C

public:
	inline void Reset(void);

	void ResetCanFlags(void)
		{
		CanK = CanM = CanStar = CanBang = CanAt = CanR = FALSE;
		}

	Bool CanOutput(void) const
		{
		return (OutFlag == OUTOK ||
				OutFlag == IMPERVIOUS ||
				OutFlag == NOSTOP);
		}

	void SetOutFlag(UserControlType New)
		{
		OutFlag = New;
		setio();
		}

	UserControlType GetOutFlag(void) const
		{
		return (OutFlag);
		}

	void ResetOutParagraph(void)
		{
		if (GetOutFlag() == OUTPARAGRAPH)
			{
			SetOutFlag(OUTOK);
			}
		}

	void SetContinuous(Bool New)
		{
		Continuous = New;
		}

	void SetCanK(Bool New)
		{
		CanK = New;
		}

	void SetCanM(Bool New)
		{
		CanM = New;
		}

	void SetCanStar(Bool New)
		{
		CanStar = New;
		}

	void SetCanR(Bool New)
		{
		CanR = New;
		}

	void SetCanBang(Bool New)
		{
		CanBang = New;
		}

	void SetCanAt(Bool New)
		{
		CanAt = New;
		}

	Bool CheckInput(Bool Pause);
	};


struct OutputControl
	{
	Bool		Formatting; 	// TRUE or FALSE
	EchoType	Echo;			// NEITHER, CALLER, or BOTH
	int 		EchoChar;		// What to echo on NEITHER
	int 		HangingIndent;	// Hanging indent. Duh.
	Bool		Modem;
	Bool		Console;
	Bool		inANSI; 		// Processing an ANSI code...
	Bool		UseMCI; 		// Is MCI Active?
	Bool		MCI_goto;		// if we are in middle of one
	label		MCI_label;		// where we are gotoing
	Bool		Psycho; 		// Psycho chicken?
	Bool		Printing;		// printing now?
	Bool		WasPrinting;	// printing before logged in?
	citFILE 	PrintFile;		// printer.out
	fpath		PrintfileName;	// name of printer file
	uchar		CrtColumn;		// current position on screen
	uchar		ansiattr;
	Bool		justdidpause;
	char		prtf_buff[512]; // for printing output
	};

struct MsgStatus
	{
	Message *AbortedMessage;	// saved message buffer
	int 	Read;				// #messages read
	int 	Entered;			// #messages entered
	ulong	MarkedID;			// ID of marked message
	messageList *MsgList;		// message queue pointer
	Bool	AutoMark;			// automatically mark message?
	Bool	AutoKill;			// automatically kill message?
	Bool	AutoCensor; 		// automatically censor message?
	};


// Room data
#define LOBBY			0		// Lobby>	is >always< room 0.
#define MAILROOM		1		// Mail>	is >always< room 1.
#define AIDEROOM		2		// Aide)	is >always< room 2.
#define DUMP			3		// Dump>	is >always< room 3.

// known stuff
#define K_APPLIC	0x00000001l
#define K_ANON		0x00000002l
#define K_PERM		0x00000004l
#define K_DIR		0x00000008l
#define K_GROUP 	0x00000010l
#define K_LOCAL 	0x00000020l
#define K_NEW		0x00000040l
#define K_OLD		0x00000080l
#define K_MAIL		0x00000100l
#define K_SHARED	0x00000200l
#define K_THALL 	0x00000400l
#define K_WINDOWS	0x00000800l
#define K_EXCLUDE	0x00001000l
#define K_NUM		0x00002000l
#define K_NOMSGS	0x00004000l
#define K_BIO		0x00008000l
#define K_HIDDEN	0x00010000l
#define K_KEYWORD	0x00020000l


// for F4 status screen
struct statRecord
	{
	char theStatus[61];
	char PAD;
	time_t theTime;
	};

// buffered file i/o stuff - must match stuff in filebuf.asm
struct fBufs
	{
	long bufstart;				// where in file this is buffer of
	uint buflen;				// how much of the file is buffered

	Bool dirty; 				// needs to be written to disk?
	Bool inuse; 				// valid data in this buffer

	uchar *buffer;				// the buffer
	};

struct BFILE
	{
	long	fpointer;			// where in file we are looking/reading
	int 	fhandle;			// file's DOS handle
	int 	eof;				// are we in an EOF state?
	uint	level;				// how far into the buffer are we?
	fBufs	buffer; 			// the actual buffer. see above.
#ifdef WINCIT
	long	DOSfpointer;		// for CloseTmp and ReOpen...
	OFSTRUCT of;
#endif
	};

// file download stuff
struct fileQueue
	{
	fileQueue *next;
	long size;
	label room;
	char fname[1];
	};

struct archivers
	{
	archivers *next;
	label name;
	char view[64];
	char extract[64];
	char ext[4];
	};

enum discardableType;

struct discardable
	{
	discardable *next;
	discardableType type;
	long length;
	void *data;
	void *aux;
	};

struct datestruct
	{
	int Year;
	MonthsE Month;
	int Date;
	};

struct timestruct
	{
	int Hour;
	int Minute;
	int Second;
	};

enum MenuNames;
enum HelpFiles;
enum BlurbFiles;
enum SexE;

enum CITEVENTS;
struct events;
struct intEvents;
class LogEntry1;

struct directoryinfo
	{
	uint	time;
	uint	date;
	long	size;
	char	attrib;
	char	name[13];
	};


class TwirlyCursorC
	{
	int Length; 					// Length of twirly string
	int Position;					// Position in the string
	Bool Active;					// Being displayed?

public:
	void Start(void);				// uimisc.cpp
	void Update(void);				// uimisc.cpp
	void Stop(void);				// uimisc.cpp
	};

class ConsoleLockC
	{
	// CON_LOCKED: Nothing works
	// CON_F6LOCKED: All but F6 (modified and unmodified) works
	// CON_UNLOCKED: Everything works
	enum ConsoleLockE
		{
		CON_LOCKED, 	CON_F6LOCKED,	CON_UNLOCKED,
		} LockState;

public:
	ConsoleLockC(void)
		{
		LockState = CON_UNLOCKED;
		}

	void Lock(void)
		{
		LockState = CON_LOCKED;
		}

	void LockF6(void)
		{
		LockState = CON_F6LOCKED;
		}

	void Unlock(void)
		{
		LockState = CON_UNLOCKED;
		}

	Bool MayUnlock(void);
	Bool IsLocked(Bool CheckingF6 = FALSE);
	};

enum MCICommandsE
	{
	MCI_REALNAME,	MCI_LASTNAME,		MCI_PHONENUM,	MCI_ADDR,
	MCI_LASTCALLD,	MCI_ACCTBAL,		MCI_ROOMNAME,	MCI_HALLNAME,
	MCI_NODENAME,	MCI_NODEPHONE,		MCI_DISKFREE,	MCI_NUMMSGS,
	MCI_CLS,		MCI_CALLNUM,		MCI_SYSOPNAME,	MCI_CONNRATE,
	MCI_PORTRATE,	MCI_MSGROOM,		MCI_BEEPNUM,	MCI_GETCHAR,
	MCI_GETSTR, 	MCI_PUTCHAR,		MCI_PUTSTR, 	MCI_BSNUM,
	MCI_OUTSPEC,	MCI_SLOW,			MCI_PASSWORD,	MCI_INITIALS,
	MCI_GOTO,		MCI_LABEL,			MCI_COMPARE,	MCI_GT,
	MCI_LT, 		MCI_EQ, 			MCI_RANDOM, 	MCI_ASSIGN,
	MCI_ADD,		MCI_SUBTRACT,		MCI_TIMES,		MCI_DIVIDE,
	MCI_ASGNNEXT,	MCI_HANGINGINDENT,

	// These are not in the MCICodes array...
	MCI_USERNAME,	MCI_FIRSTNAME,	MCI_TIME,		MCI_DATE,
	MCI_POOP,

	MCI_NUMCODES,
	};

class MCIRecursionCheckerC
	{
	Bool MCIDisabled[MCI_NUMCODES];
	Bool MCIBusy[MCI_NUMCODES];

public:
	void EnableAll(void)
		{
		memset(MCIDisabled, 0, sizeof(MCIDisabled));
		}

	MCIRecursionCheckerC(void)
		{
		EnableAll();
		memset(MCIBusy, 0, sizeof(MCIBusy));
		}

	Bool Start(MCICommandsE WhichOne);	// mci.cpp

	void End(MCICommandsE WhichOne)
		{
		assert(WhichOne >= (MCICommandsE) 0);
		assert(WhichOne < MCI_NUMCODES);

		assert(MCIBusy[WhichOne]);
		MCIBusy[WhichOne] = FALSE;
		}

	void SetEnabled(MCICommandsE WhichOne, Bool Enable)
		{
		assert(WhichOne >= (MCICommandsE) 0);
		assert(WhichOne < MCI_NUMCODES);

		MCIDisabled[WhichOne] = !Enable;
		}
	};

class ScreenSaverC
	{
	long Timer; 					// time of last key hit
	Bool On;						// if it is turned on
	Bool MayTurnOn; 				// If it is allowed.

public:
	ScreenSaverC(void)
		{
		Timer = time(NULL);
		On = FALSE;
		MayTurnOn = FALSE;
		}

	Bool IsOn(void) const
		{
		return (On);
		}

	void Check(void);
	Bool TurnOn(void);
	void TurnOff(void);

	void Update(void)
		{
		Timer = time(NULL);

		if (On)
			{
			TurnOff();
			}
		}

	void SetMayTurnOn(Bool New)
		{
		MayTurnOn = !!New;
		}
	};

enum StatusLineStateE
	{
	SL_ONELINE, 		SL_TWOLINES,		SL_FULLSCREEN,
	};

class StatusLineC
	{
	StatusLineStateE State; 		// 1, 2, or full-screen
	Bool Visible;					// Blanked?

	time_t HelpTimeout; 			// Time of take-down (0 = Not up)
public:
	StatusLineC(void)
		{
		State = SL_ONELINE;
		Visible = FALSE;
		HelpTimeout = 0;
		};

	Bool IsVisible(void) const
		{
		return (Visible);
		}

	int Height(void) const
		{
		int h = 0;

		if (Visible)
			{
			h = 1;

			if (HelpTimeout)
				{
				h += 4;
				}

			if (State == SL_TWOLINES)
				{
				h++;
				}
			}

		return (h);
		}

	StatusLineStateE GetState(void) const
		{
		return (State);
		}

	Bool IsFullScreen(void) const
		{
		return (State == SL_FULLSCREEN);
		}

	void Update(void);
	void ToggleHelp(void);
	void Toggle(void);
	void ToggleSecond(void);
	Bool ToggleFullScreen(void);
	};

class KeyboardBufferC
	{
	char Buffer[KEYBUFSIZE];
	int Start, End;
	Bool Dirty;

	strList *InsertedStrings;

	char filterChar(const char In)
		{
		if (In == '\n')
			{
			return ('\r');
			}

		return (In);
		}

public:
	void Flush(void)
		{
void disposeLL(void **list);	// from proto.h
		disposeLL((void **) &InsertedStrings);

		Start = End = 0;
		Dirty = TRUE;
		}

	KeyboardBufferC(void)
		{
		InsertedStrings = NULL;
		Flush();
		}

	Bool HasInsertedStrings(void) const
		{
		return (!!InsertedStrings);
		}

	Bool IsFull(void) const
		{
		return ((End + 1) % KEYBUFSIZE == Start);
		}

	Bool Add(char NewKey)
		{
		NewKey = filterChar(NewKey);

		if (!IsFull())
			{
			Buffer[End] = NewKey;
			End = (End + 1) % KEYBUFSIZE;
			Dirty = TRUE;
			return (TRUE);
			}
		else
			{
			return (FALSE);
			}
		}

	Bool InsertString(const char *NewString);

	Bool IsEmpty(void) const
		{
		return (!InsertedStrings && (Start == End));
		}

	Bool IsDirty(void) const
		{
		return (Dirty);
		}

	void SetDirty(Bool NewDirt = TRUE)
		{
		Dirty = NewDirt;
		}

	char Peek(void) const
		{
		if (IsEmpty())
			{
			return (0);
			}
		else
			{
			if (InsertedStrings)
				{
				return (*InsertedStrings->string);
				}
			else
				{
				return (Buffer[Start]);
				}
			}
		}

	char Retrieve(void);
	};

EXTERN void *comDriver;
EXTERN label cdDesc;
EXTERN void cdecl (*initrs)(int Port, int Baud, int Stops, int Parity,
		int Length, int cCts);
EXTERN void cdecl (*deinitrs)(void);
EXTERN void cdecl (*flushrs)(void);
EXTERN int cdecl (*statrs)(void);
EXTERN int cdecl (*getrs)(void);
EXTERN void cdecl (*putrs)(char ch);
EXTERN void cdecl (*dtrrs)(int dtr);
EXTERN int cdecl (*carrstatrs)(void);
EXTERN int cdecl (*ringstatrs)(void);
EXTERN void cdecl (*flushoutrs)(void);

class SerialPortC
	{
	Bool Initialized;

	ulong Transmitted;				// # characters transmitted
	ulong Received; 				// # characters received

	Bool Disabled;					// Or is it differently abled?

	PortSpeedE Speed;

	uchar BeenSent[KEYBUFSIZE];
	int SentStart, SentEnd, NumBeenSent;

	uchar BeenReceived[KEYBUFSIZE];
	int RecStart, RecEnd, NumBeenReceived;

	Bool Dirty;

public:
	SerialPortC(void)
		{
		Initialized = FALSE;
		Transmitted = Received = 0;
		Disabled = TRUE;
		Speed = PS_300;
		SentStart = SentEnd = RecStart = RecEnd = NumBeenSent =
				NumBeenReceived = 0;
		Dirty = FALSE;
		}

	Bool IsDirty(void) const
		{
		return (Dirty);
		}

	void SetDirty(Bool NewDirt = TRUE)
		{
		Dirty = NewDirt;
		}

	void Disable(void)
		{
		Disabled = TRUE;
		}

	void Enable(void)
		{
		Disabled = FALSE;
		}

	Bool IsDisabled(void)
		{
		return (Disabled);
		}

	Bool IsRingIndicator(void) const
		{
		assert(ringstatrs);
		return ((*ringstatrs)());
		}

	Bool IsInputReady(void) const
		{
		assert(statrs);
		return ((*statrs)());
		}

	void FlushOutput(void)
		{
		assert(flushoutrs);
		(*flushoutrs)();
		}

	void RaiseDtr(void) const
		{
		assert(dtrrs);
		(*dtrrs)(1);
		}

	Bool HaveCarrier(void) const
		{
		assert(carrstatrs);
#ifdef SOFTPC
		return (FALSE);
#else
		return ((*carrstatrs)());
#endif
		}

	uchar Input(void)
		{
		assert(getrs);
		uchar ToRet = (*getrs)();
		Received++;

		SetDirty();

		BeenReceived[RecEnd] = ToRet;
		RecEnd = (RecEnd + 1) % KEYBUFSIZE;
		if (RecEnd == RecStart)
			{
			RecStart = (RecStart + 1) % KEYBUFSIZE;
			}
		else
			{
			NumBeenReceived++;
			}

		return (ToRet);
		}

	int GetNumInSentBuffer(void) const
		{
		return (NumBeenSent);
		}

	int GetNumInReceivedBuffer(void) const
		{
		return (NumBeenReceived);
		}

	PortSpeedE GetSpeed(void) const
		{
		return (Speed);
		}

	void Deinit(void)
		{
		Initialized = FALSE;
		assert(deinitrs);
		(*deinitrs)();
		}

	void ResetReceived(void)
		{
		Received = 0;
		}

	void ResetTransmitted(void)
		{
		Transmitted = 0;
		}

	void IncReceived(void)
		{
		Received++;
		}

	void IncTransmitted(void)
		{
		Transmitted++;
		}

	ulong GetReceived(void) const
		{
		return (Received);
		}

	ulong GetTransmitted(void) const
		{
		return (Transmitted);
		}

	uchar GetSentChar(int Num) const;
	uchar GetReceivedChar(int Num) const;

	void DropDtr(void);
	void SetSpeed(PortSpeedE NewSpeed);
	void Output(uchar Out);
	void Flush(void);

	uchar GetFromSentBuffer(int Num) const
		{
		return (BeenSent[(SentStart + Num - 1) % KEYBUFSIZE]);
		}

	uchar GetFromReceivedBuffer(int Num) const
		{
		return (BeenReceived[(RecStart + Num - 1) % KEYBUFSIZE]);
		}

	void Init(void)
		{
		SetSpeed(Speed);
		}
	};

// Citadel can be told to do certain things after waiting a certain
// amount of time during certain hours of certain days of certain
// months and certainly this is getting hard to follow. These events
// called Cron Events, are read from CRON.CIT and stored in the this
// structure, which forms a linked list in memory. Because this is
// time dependent, Citadel also stores this information in CRON.TAB
// when it exits.


// This enumeration is supposed to be used to tell the Cron code why
// it is being called. However, the only one that is ever used is
// CRON_TIMEOUT. The other two might be implemented some time, but I
// really doubt it.
enum CronCallerE
	{
	CRON_TIMEOUT,		CRON_LOGOUT,		CRON_PROMPT,
	};

enum CronKeywordsE;
enum CronTypesE;

class CronEventC
	{
	CronTypesE Type;			// Event type (network, shell, etc.)
	label String;				// nodename, shell command, etc

	int RedoTime;				// minutes before it will redo
	int RetryTime;				// minutes before it will retry

	long LastSuccess;			// last time it has success
	long LastTried; 			// last time it tried

	Bool Zapped;				// was this event zapped?

	char Months[NUM_MONTHS];	// valid months for event
	char Dates[31]; 			// valid dates for event
	char Hours[24]; 			// valid hours for event
	char Days[NUM_DAYS];		// valid days for event

public:
	CronEventC(void)
		{
		memset(this, 0, sizeof(*this));
		}

	CronTypesE GetType(void) const
		{
		assert(this);
		return (Type);
		}

	void Reset(void)
		{
		LastSuccess = LastTried = 0;
		Zapped = FALSE;
		}

	void ToggleZap(void)
		{
		assert(this);
		Zapped = !Zapped;
		}

	Bool IsZapped(void) const
		{
		return (Zapped);
		}

	void SetString(const char *NewStr)
		{
		CopyStringToBuffer(String, NewStr, LABELSIZE);
		}

	const char *GetString(void) const
		{
		return (String);
		}

	void SetType(CronTypesE NewType)
		{
		assert(this);
		Type = NewType;
		}

	Bool CanDo(Bool IgnoreTime = FALSE) const;

	void SetRedo(int NewRedo)
		{
		assert(this);
		RedoTime = NewRedo;
		}

	void SetRetry(int NewRetry)
		{
		assert(this);
		RetryTime = NewRetry;
		}

	int GetRedo(void) const
		{
		assert(this);
		return (RedoTime);
		}

	int GetRetry(void) const
		{
		assert(this);
		return (RetryTime);
		}

	void SetLastSuccess(time_t Time = 0)
		{
		assert(this);
		if (!Time)
			{
			Time = time(NULL);
			}

		LastSuccess = Time;
		}

	void SetLastTried(time_t Time = 0)
		{
		assert(this);
		if (!Time)
			{
			Time = time(NULL);
			}

		LastTried = Time;
		}

	void SetDone(void)
		{
		SetLastSuccess();
		SetLastTried(LastSuccess);
		}

	time_t GetLastSuccess(void) const
		{
		assert(this);
		return (LastSuccess);
		}

	time_t GetLastTried(void) const
		{
		assert(this);
		return (LastTried);
		}

	Bool LastAttemptFailed(void) const
		{
		return (LastTried != LastSuccess);
		}

	void SetMonth(MonthsE NewMonth, Bool Okay)
		{
		assert(this);
		assert(NewMonth >= (MonthsE) 0 && NewMonth < NUM_MONTHS);
		Months[NewMonth] = Okay;
		}

	void SetDate(int NewDate, Bool Okay)
		{
		assert(this);
		assert(NewDate > 0 && NewDate < 32);
		Dates[NewDate - 1] = Okay;
		}

	void SetHour(int NewHour, Bool Okay)
		{
		assert(this);
		assert(NewHour >= 0 && NewHour <= 23);
		Hours[NewHour] = Okay;
		}

	void SetDay(DaysE NewDay, Bool Okay)
		{
		assert(this);
		assert(NewDay >= (DaysE) 0 && NewDay < NUM_DAYS);
		Days[NewDay] = Okay;
		}

	Bool Do(void);
	Bool Store(FILE *file);
	Bool Load(FILE *file);
	};

struct CronEventListS
	{
	CronEventListS *next;
	CronEventC Event;
	};


class CronC
	{
	time_t StartTime;		// When to count from

	Bool Pause; 			// .SCP state

	CronEventListS *Events;
	uint NumEvents;
	CronEventListS *OnEvent;

	CronEventListS *ExecutingEvent;

public:
	CronC(void)
		{
		StartTime = time(NULL);
		Pause = FALSE;
		Events = OnEvent = NULL;
		NumEvents = 0;
		}

	void ResetTimer(void)
		{
		StartTime = time(NULL);
		}

	void SetPause(Bool NewP = TRUE)
		{
		Pause = NewP;
		}

	CronEventListS *AddEvent(void);
	void DumpEvents(void);

	void TogglePause(void)
		{
		Pause = !Pause;
		}

	Bool HasEvents(void) const
		{
		return (Events != NULL);
		}

	Bool IsOnEvent(const CronEventListS *Test) const
		{
		return (OnEvent == Test);
		}

	void SetOnEvent(CronEventC *Set);

	void SetOnEvent(CronEventListS *Set)
		{
		OnEvent = Set ? Set : Events;
		}

	Bool IsPaused(void) const
		{
		return (Pause);
		}

	uint GetNumEvents(void) const
		{
		return (NumEvents);
		}

	time_t GetStartTime(void) const
		{
		return (StartTime);
		}

	const CronEventListS *GetExecutingEvent(void) const
		{
		return (ExecutingEvent);
		}

	Bool IsReady(void) const
		{
		return ((((int) (time(NULL) - StartTime)) / 60) >= cfg.idle);
		}

	CronEventListS *GetEventNum(int Number);
	Bool Add(const CronEventC &NewEvent);
	const CronEventC *GetEvent(int Num) const;
	Bool Do(CronCallerE WhyCall);

	Bool WriteTable(void);
	Bool ReadTable(void);
	Bool ReadCronCit(void);
	};

struct CITWINDOW;

enum OSTypeE
	{
	OS_DOS, 	OS_OS2, 	OS_DV,		OS_WINS,		OS_WIN3,
	OS_NUM
	};


struct ScriptInfoS; 	// script.h for full definition


#ifdef MULTI
	#define TI()	ti->
	#define tiEXT

	struct taskInfo
		{
#else
	#define TI()
	#define tiEXT	EXTERN

#endif

tiEXT	ReadFilter	rf; 			// read filter
tiEXT	ReadFilter	rf2;			// Saved read filter
tiEXT	ModemSpeedE ModemSpeed; 	// the modem<=>modem speed
tiEXT	Bool UsingCompression;
tiEXT	Bool UsingCorrection;

tiEXT	char		prevChar;		// for EOLN/EOParagraph stuff
tiEXT	char		PAD;
tiEXT	long		logtimestamp;	// when someone last logged in
tiEXT	long		conntimestamp;	// when connection was made
tiEXT	UserAccountInfo *CurrentUserAccount;// Groupdata for current user
tiEXT	NodeCitInfo *node;			// node buffer
tiEXT	terminal	term;			// terminal info
tiEXT	Bool		chatReq;		// Did they want to chat?
tiEXT	h_slot		thisHall;		// hall we're in
tiEXT	LogEntry	*CurrentUser;	// Task dependent log stuff.
tiEXT	l_index 	ThisLog;		// where in file
tiEXT	l_slot		ThisSlot;		// where in table
tiEXT	Bool		loggedIn;		// Global have-caller flag
tiEXT	Bool		modStat;		// Had carrier LAST time checked
tiEXT	Bool		sleepkey;		// Alt-Z pressed
tiEXT	Bool		chatkey;		// F8 or Alt-C pressed
tiEXT	Bool		showdir;		// for .K... if !expert
tiEXT	Bool		showhidden; 	// for .K... if !expert
tiEXT	Bool		showbio;		// for .K... if !expert
tiEXT	Bool		showgroup;		// for .K... if !expert
tiEXT	uint		roomsMade;		// # rooms made this call
tiEXT	char		gprompt[256];
tiEXT	label		modem_result;	// the result from dialing out
tiEXT	int 		numLines;
tiEXT	long		wowcount;		// wowcount

tiEXT	int 		logiRow, logiCol;// Cursor position


tiEXT	MsgReadOptions	MRO;		// .R!, .R&, etc.
tiEXT	MsgStatus	MS; 			// ...

tiEXT	Bool		callout;		// Always send to modem if TRUE.

tiEXT	int 		duplicate;		// count net duplicates
tiEXT	int 		expired;		// count net expired
tiEXT	Bool		netError;		// save net error message?

tiEXT	Bool		menu69; 		// in .S6... menu

tiEXT	Bool		netFailed;		// Did netting fail?
tiEXT	fileQueue	*fileList;		// .Q...
tiEXT	long		altF3Timeout;	// When will timeout
tiEXT	char		chatsubject;
tiEXT	label		MCI_str[10];	// String variables
tiEXT	char		MCI_char[10];	// Character variables
tiEXT	int 		MCI_result; 	// Last result.
tiEXT	time_t		LastActiveTime; // The last time the user did anything
tiEXT	r_slot		thisRoom;		// Which room user is in.
tiEXT	void		*roomInfo;		// Data private to Room subsystem
#ifdef WINCIT
tiEXT	void huge	*talleyInfo;	// Data private to Talley Buffer
#else
tiEXT	void		*talleyInfo;	// Data private to Talley Buffer
#endif
tiEXT	Message 	*AideMsg;		// For aide message being created
tiEXT	ModemConsoleE whichIO;		// Who is using the system?
tiEXT	Bool		ExitToMsdos;	// bring system down
tiEXT	dowhattype	DoWhat; 		// What this task is doing
tiEXT	Bool		showPrompt; 	// should show the prompt?
tiEXT	Bool		seen;
tiEXT	Bool		displayedmessage;

tiEXT	UserOutputControl UserControl;// Jumping and Nexting and poop.

tiEXT	OutputControl	OC; 		// non-user control of Citadel's output

tiEXT	char		*logiScreen;	// logical address of screen
tiEXT	char		*saveBuffer;	// buffer for screen saves

tiEXT	TwirlyCursorC TwirlyCursor; // The twirly cursor.

tiEXT	MCIRecursionCheckerC MCIRecursionChecker;	// Wow.

tiEXT	char		ContextSensitiveScriptText[256];
tiEXT	KeyboardBufferC KeyboardBuffer; // The keyboard buffer.

tiEXT	CITWINDOW	*KeyboardBufferMonitor; // It's monitor window.
tiEXT	CITWINDOW	*SerialPortMonitor; // It's monitor window.
tiEXT	CITWINDOW	*CronMonitor;	// It's monitor window.

tiEXT	SerialPortC SerialPort; 	// The serial port, of course.

tiEXT	Bool AllowESC;				// For EDIT.CPP

tiEXT	Message *MsgScriptMsg;

	// DOMISC.CPP
tiEXT	int 		Ycounter;		// for doY()

	// strftime.cpp
tiEXT	MonthsE 	lastMonthHoliday;// The last month we checked
tiEXT	int 		lastDateHoliday;// The last date we checked
tiEXT	holidays	*curHoliday;	// Pointer to all of the data

	// output.cpp
tiEXT	Bool		UpDoWn; 		// You do not want to know

	// carrier.cpp
tiEXT	long		lastringtime;	// Time of last RING from modem

	// misc.cpp
tiEXT	uint		borderCounter;	// Counts between borders
tiEXT	uint		lastBorder; 	// Last one displayed

tiEXT	Bool		MCI_asgnnext;	// Assign next MCI to a variable?
tiEXT	int 		MCI_asgnvar;	// Which one?

	// format.cpp
tiEXT	char		fmtBuf[MAXWORD + 1];// For various routines

	// outovl.cpp
tiEXT	Bool		PLfirst;		// For prtList()
tiEXT	Bool		PLlisted;		// For prtList()

	// window.cpp
tiEXT	char		ANSIargs[ANSILEN];// current ANSI arguments
tiEXT	Bool		ANSIfirst;		// Beginning of ANSI
tiEXT	Bool		ANSIhilight;	// hilight is on
tiEXT	Bool		ANSIblink;		// blinking is on
tiEXT	Bool		ANSIisSound;	// Sound output
tiEXT	int 		ANSIc_x;		// stored X position
tiEXT	int 		ANSIc_y;		// stored Y position

	// roomlow.cpp
tiEXT	r_slot		lastFoundRoom;	// For roomExists()

	// ctdl.cpp
tiEXT	Bool		hitSix; 		// for doRegular()

	// netmail.cpp
tiEXT	char		netMailAddress[9];// for makeaddress() & oaddress()

	// files.cpp
tiEXT	char		dlTimeBuff[10]; // for dltime()

	// term.cpp
tiEXT	char		attrToANSIstr[15];// for attrtoansi()
tiEXT	Bool		termCaphilight; // for termCap()
tiEXT	Bool		termCapblink;	// for termCap()
tiEXT	Bool		termCapdirty;	// for termCap()

	// timedate.cpp
tiEXT	label		diffstampstr;	// for diffstamp()

	// help.cpp
tiEXT	int 		ansiChat;		// for nochat()
tiEXT	int 		normChat;		// for nochat()

	// grpmembr.cpp
tiEXT	const LogEntry2 *Log2ForGroupTester;
tiEXT	l_slot SlotForGroupTester;
tiEXT	const char *GroupNameForNameTester;

#ifdef MULTI
		};
#endif

#define onConsole	(TI()whichIO == CONSOLE)

#define CTDL
#include "rlm.h"

#include "keycodes.h"
#include "global.h"
#include "proto.h"

#ifndef WINCIT
#include "spawno.h"
#endif

#ifndef __BORLANDC__
	// copyright violation as you watch.
inline int random(int __num)
		{ return(int)(((long)rand()*__num)/((uint)RAND_MAX+1)); }
#endif
