// --------------------------------------------------------------------------
// Citadel: Status.CPP
//
// The .Read Status command and F4 function

#include "ctdl.h"
#pragma hdrstop

#include "room.h"
#include "log.h"
#include "group.h"
#include "msg.h"
#include "account.h"
#include "hall.h"
#include "net.h"
#include "roompriv.h"

#if defined(__BORLANDC__) && !defined(WINCIT) && (VERSION != RELEASE)
	#define DEBUGMEM	1
	#include "tallybuf.h"
	#include "script.h"
#else
	#define DEBUGMEM	0
//? #include "cwindows.h"
#endif


// --------------------------------------------------------------------------
// Contents
//
// ShowSystemStatus()	The .Read Status command


#if DEBUGMEM
extern uint far cdecl _OvrHeapOrg;
extern char ** cdecl environ;
extern uint cdecl _envSize;

// P1 is the start of allocated memory, P2 is the pointer we are checking
// it against.
static Bool CloseEnough(const void *P1, const void *P2)
	{
	// Borland C++ might start an array 4 bytes after the start of its
	// allocated memory. (Those four bytes contain the number of elements
	// in the array; I suppose this is used when deleting an array, so it
	// knows how many destructors to issue for the array.) So, we check
	// for both being at the start of memory or being 4 bytes beyond it.
	// Also note that we don't do the "four bytes beyond" check if this
	// is NULL.
	return ((P1 == P2) || (P1 && (((long) P1) + 4) == (long) P2));
	}

static void CheckArrayPtr(const void *p, const void *Check, const char *Name, int i)
	{
	if (CloseEnough(Check, p))
		{
		rmPrintf(" (%s[%d])", Name, i);
		}
	}

static void CheckLLPtr(const void *LL, const void *Check, const char *Name)
	{
	if (Check)
		{
		int i;

		for (i = 0; LL; LL = getNextLL(LL), i++)
			{
			if (CloseEnough(Check, LL))
				{
				rmPrintf(" (%s[%d])", Name, i);
				}
			}
		}
	else	// NULL
		{
		if (CloseEnough(NULL, LL))
			{
			rmPrintf(" (%s[])", Name);
			}
		}
	}

void CheckDiscardablePtr(const discardable *D, const void *Check, const char *Name)
	{
	if (Check)
		{
		int i;

		for (i = 0; D; D = (discardable *) getNextLL(D), i++)
			{
			if (CloseEnough(Check, D))
				{
				rmPrintf(" (%s[%d])", Name, i);
				}

			if (CloseEnough(Check, D->data))
				{
				rmPrintf(" (%s[%d].data)", Name, i);
				}

			if (CloseEnough(Check, D->aux))
				{
				rmPrintf(" (%s[%d].aux)", Name, i);
				}
			}
		}
	else	// NULL
		{
		if (CloseEnough(NULL, D))
			{
			rmPrintf(" (%s[])", Name);
			}
		}
	}

static void CheckRLMListPtr(const RLMlist *L, const void *Check, const char *Name)
	{
	if (Check)
		{
		int i;

		for (i = 0; L; L = (RLMlist *) getNextLL(L), i++)
			{
			if (CloseEnough(Check, L))
				{
				rmPrintf(" (%s[%d])", Name, i);
				}

			if (CloseEnough(Check, L->where))
				{
				rmPrintf(" (%s[%d].where)", Name, i);
				}

			if (CloseEnough(Check, L->permData))
				{
				rmPrintf(" (%s[%d].permData)", Name, i);
				}

			if (CloseEnough(Check, L->loadArea))
				{
				rmPrintf(" (%s[%d].loadArea)", Name, i);
				}

			if (L->loadArea && CloseEnough(Check, L->loadArea->tmpData))
				{
				rmPrintf(" (%s[%d].loadArea->tmpData)", Name, i);
				}
			}
		}
	else	// NULL
		{
		if (CloseEnough(NULL, L))
			{
			rmPrintf(" (%s[])", Name);
			}
		}
	}

#ifdef AUXMEM
static void CheckAuxmemPtr(const auxTabList *A, const void *Check, const char *Name)
	{
	if (Check)
		{
		int i;

		for (i = 0; A; A = (auxTabList *) getNextLL(A), i++)
			{
			if (CloseEnough(Check, A))
				{
				rmPrintf(" (%s[%d])", Name, i);
				}

			if (A->whatMem == atHEAP && CloseEnough(Check, MkPtr(A->Where)))
				{
				rmPrintf(" (%s[%d].Where)", Name, i);
				}
			}
		}
	else	// NULL
		{
		if (CloseEnough(NULL, A))
			{
			rmPrintf(" (%s[])", Name);
			}
		}
	}
#endif
#endif


// --------------------------------------------------------------------------
// ShowSystemStatus(): The .Read Status command.

void ShowSystemStatus(void)
{
#ifdef MSC
	union REGS r;
#endif
	int i;
	int percentfull;

#ifdef AUXMEM
	msgflags *lmf;
#endif

	m_slot tablesize, j;

	long average, work;
	char dtstr[80];
	int publicRoom	= 0,	// tallys...
		active		= 0,
		directory	= 0,
		shared		= 0,
		hidden		= 0,
		anon		= 0,
		group		= 0,
		problem 	= 0,
		perm		= 0,
		aides		= 0,
		sysops		= 0,
		nodes		= 0,
		moderated	= 0,
		bio 		= 0;

	ulong public_mess, networked_mess, private_mess, moderated_mess;
	ulong group_mess, copies_mess, problem_mess, massemail_mess;
	ulong local_mess, censored_mess;
	int ourtimeout;

	SetDoWhat(READSTATUS);

	TI()UserControl.SetOutFlag(OUTOK);

	// On...
	doCR();
	rmPrintf(getmsg(714), cfg.nodeTitle, cfg.nodeRegion, cfg.nodeCountry,
			cfg.alias, cfg.locID);
	doCR();

	rmPrintf(getmsg(715), cfg.softverb, cfg.softverb[0] ? spc : ns,
			programName, version);
	doCR();

	rmPrintf(getmsg(507), Author);
	doCR();

	if (TI()MRO.Verbose)
		{
		rmPrintf(getmsg(508), cmpDate, cmpTime);
		doCR();
		}

	// times..
	TI()UserControl.ResetOutParagraph();

	strftime(dtstr, 79, (TI()loggedIn) ? TI()CurrentUser->GetVerboseDateStamp() :
			cfg.vdatestamp, 0l);
	doCR();
	rmPrintf(getmsg(509), (TI()loggedIn) ? getmsg(1460) :
			TI()MRO.Verbose ? spc : ns, dtstr);
	doCR();

	if (TI()MRO.Verbose)
		{
		rmPrintf(getmsg(510), (TI()loggedIn) ? getmsg(1486) : ns,
				diffstamp(uptimestamp));
		doCR();
		}

	if (SerialPort.HaveCarrier())
		{
		rmPrintf(getmsg(511), (TI()loggedIn) ? getmsg(1486) : ns,
				diffstamp(TI()conntimestamp));

		if (TI()loggedIn)
			{
			rmPrintf(getmsg(519), diffstamp(TI()logtimestamp));
			}

		doCR();
		}
	else
		{
		if (TI()loggedIn)
			{
			rmPrintf(getmsg(524), diffstamp(TI()logtimestamp));
			doCR();
			}
		}

	// Userlog info.
	TI()UserControl.ResetOutParagraph();

	doCR();
	rmPrintf(getmsg(716), cfg.Uuser_nym);

	active = aides = sysops = problem = perm = nodes = 0;

	for (i = 0; i < cfg.MAXLOGTAB; i++)
		{
		if (LogTab->GetEntry(i)->IsInuse())
			{
			active++;
			if (LogTab->GetEntry(i)->IsAide())		aides++;
			if (LogTab->GetEntry(i)->IsSysop()) 	sysops++;
			if (LogTab->GetEntry(i)->IsProblem())	problem++;
			if (LogTab->GetEntry(i)->IsPermanent()) perm++;
			if (LogTab->GetEntry(i)->IsNode())		nodes++;
			}
		}

	doCR();
	label MaxLogTab, ActiveLogTab;
	strcpy(MaxLogTab, ltoac(cfg.MAXLOGTAB));
	strcpy(ActiveLogTab, ltoac(active));

	rmPrintf(getmsg(717), MaxLogTab, ActiveLogTab, ltoac(cfg.callno));
	doCR();

	if (TI()MRO.Verbose)
		{
		rmPrintf(getmsg(974), ltoac(nodes), (nodes == 1) ? ns : justs);

		if (TI()CurrentUser->IsAide() || TI()CurrentUser->IsSysop())
			{
			label Aides, Sysops, Twits;
			strcpy(Aides, ltoac(aides));
			strcpy(Sysops, ltoac(sysops));
			strcpy(Twits, ltoac(problem));

			rmPrintf(getmsg(555), Aides, (aides == 1) ? ns : justs,
					Sysops, (sysops == 1) ? ns : justs, Twits,
					(problem == 1) ? cfg.Luser_nym : cfg.Lusers_nym,
					ltoac(perm), (perm == 1) ? cfg.Luser_nym : cfg.Lusers_nym);
			}

		doCR();
		rmPrintf(getmsg(556), cfg.Luser_nym, cfg.poopuser,
				ltoac(cfg.maxpoop));
		doCR();
		}

	// Group info
	TI()UserControl.ResetOutParagraph();

	doCR();
	rmPrintf(getmsg(718), cfg.Ugroup_nym);

	for (active = 0, anon = 0, hidden = 0, nodes = 0, i = 0;
			i < cfg.maxgroups; ++i)
		{
		if (GroupData->GetEntry(i)->IsInuse())
			{
			active++;

			if (GroupData->GetEntry(i)->IsAutoAdd())
				{
				anon++;
				}

			if (GroupData->GetEntry(i)->IsHidden())
				{
				hidden++;
				}

			if (GroupData->GetEntry(i)->IsLocked())
				{
				nodes++;
				}
			}
		}
	doCR();
	label MaxGroups;
	strcpy(MaxGroups, ltoac(cfg.maxgroups));

	rmPrintf(getmsg(1290), MaxGroups, cfg.Lgroups_nym, ltoac(active));

	if (TI()MRO.Verbose)
		{
		label AutoAdd, Hidden;
		strcpy(AutoAdd, ltoac(anon));
		strcpy(Hidden, ltoac(hidden));

		rmPrintf(getmsg(1291), AutoAdd, Hidden, ltoac(nodes));
		}
	doCR();

	// Hall info
	TI()UserControl.ResetOutParagraph();

	doCR();
	rmPrintf(getmsg(718), cfg.Uhall_nym);

	for (anon = 0, group = 0, active = 0, i = 0; i < cfg.maxhalls; ++i)
		{
		if (HallData->GetEntry(i)->IsInuse())
			{
			active++;

			if (HallData->GetEntry(i)->IsOwned())
				{
				group++;
				}

			if (HallData->GetEntry(i)->IsEnterRoom())
				{
				anon++;
				}
			}
		}

	doCR();
	label MaxHalls;
	strcpy(MaxHalls, ltoac(cfg.maxhalls));

	rmPrintf(getmsg(722), MaxHalls, cfg.Lhalls_nym, ltoac(active));

	if (TI()MRO.Verbose)
		{
		label Group;
		strcpy(Group, ltoac(group));

		rmPrintf(getmsg(723), Group, cfg.Lgroup_nym, ltoac(anon),
				cfg.Lroom_nym);
		}
	doCR();

	// Room info
	TI()UserControl.ResetOutParagraph();

	doCR();
	rmPrintf(getmsg(718), cfg.Uroom_nym);

	anon = active = publicRoom = hidden = directory = group = shared =
			moderated = 0;

	for (i = 0; i < cfg.maxrooms; i++)
		{
		if (IsRoomInuse(i))
			{
			active++;
			if (!IsRoomHidden(i))
				{
				if (!IsRoomGroupOnly(i) && !IsRoomBIO(i))
					{
					publicRoom++;
					}
				}
			else
				{
				hidden++;
				}

			if (IsRoomBIO(i))
				{
				bio++;
				}

			if (IsRoomDirectory(i))
				{
				directory++;
				}

			if (IsRoomGroupOnly(i))
				{
				group++;
				}

			if (IsRoomShared(i))
				{
				shared++;
				}

			if (IsRoomModerated(i))
				{
				moderated++;
				}

			if (IsRoomAnonymous(i))
				{
				anon++;
				}
			}
		}

	doCR();
	label MaxRooms;
	strcpy(MaxRooms, ltoac(cfg.maxrooms));
	rmPrintf(getmsg(719), MaxRooms, cfg.Lrooms_nym, ltoac(active));
	doCR();

	if (TI()MRO.Verbose)
		{
		label Public, Hidden, Bio, Shared, Directory, Moderated, Group;
		strcpy(Public, ltoac(publicRoom));
		strcpy(Hidden, ltoac(hidden));
		strcpy(Bio, ltoac(bio));
		strcpy(Shared, ltoac(shared));
		strcpy(Directory, ltoac(directory));
		strcpy(Moderated, ltoac(moderated));
		strcpy(Group, ltoac(group));

		rmPrintf(getmsg(720), Public, Hidden, Bio, Shared, Directory,
				Moderated, Group, cfg.Lgroup_nym, ltoac(anon));
		doCR();
		}

	// Message status
	TI()UserControl.ResetOutParagraph();

	doCR();
	rmPrintf(getmsg(718), cfg.Umsg_nym);
	doCR();

	// stop before the message count if already aborted...
	if (TI()UserControl.GetOutFlag() != OUTOK)
		{
		return;
		}

	if (TI()MRO.Verbose)
		{
		public_mess 	=
		networked_mess	=
		private_mess	=
		moderated_mess	=
		group_mess		=
		copies_mess 	=
		massemail_mess	=
		censored_mess	=
		local_mess		=
		problem_mess	= 0;

		tablesize = (m_slot) sizetable();

		for (j = 0; j < tablesize; ++j)
			{
#ifdef AUXMEM
			lmf = getFlags(j);

			if (lmf->IsNetworked()) networked_mess++;
			if (lmf->IsCopy())		copies_mess++;
			if (lmf->IsProblem())	problem_mess++;
			if (lmf->IsModerated()) moderated_mess++;
			if (lmf->IsCensored())	censored_mess++;
			if (lmf->IsLocal()) 	local_mess++;

			if (lmf->IsLimited())
				{
				group_mess++;
				}
			else if (lmf->IsMail())
				{
				private_mess++;
				}
			else if (lmf->IsMassemail())
				{
				massemail_mess++;
				}
			else
				{
				public_mess++;
				}
#else
#ifdef WINCIT
			if (msgTabWin[j].mtmsgflags.IsNetworked())	networked_mess++;
			if (msgTabWin[j].mtmsgflags.IsCopy())		copies_mess++;
			if (msgTabWin[j].mtmsgflags.IsProblem())	problem_mess++;
			if (msgTabWin[j].mtmsgflags.IsModerated())	moderated_mess++;
			if (msgTabWin[j].mtmsgflags.IsCensored())	censored_mess++;
			if (msgTabWin[j].mtmsgflags.IsLocal())		local_mess++;

			if (msgTabWin[j].mtmsgflags.IsLimited())
				{
				group_mess++;
				}
			else if (msgTabWin[j].mtmsgflags.IsMail())
				{
				private_mess++;
				}
			else if (msgTabWin[j].mtmsgflags.IsMassemail())
				{
				massemail_mess++;
				}
			else
				{
				public_mess++;
				}
#else
			if (msgTab_mtmsgflags[j].IsNetworked()) networked_mess++;
			if (msgTab_mtmsgflags[j].IsCopy())		copies_mess++;
			if (msgTab_mtmsgflags[j].IsProblem())	problem_mess++;
			if (msgTab_mtmsgflags[j].IsModerated()) moderated_mess++;
			if (msgTab_mtmsgflags[j].IsCensored())	censored_mess++;
			if (msgTab_mtmsgflags[j].IsLocal()) 	local_mess++;

			if (msgTab_mtmsgflags[j].IsLimited())
				{
				group_mess++;
				}
			else if (msgTab_mtmsgflags[j].IsMail())
				{
				private_mess++;
				}
			else if (msgTab_mtmsgflags[j].IsMassemail())
				{
				massemail_mess++;
				}
			else
				{
				public_mess++;
				}
#endif
#endif
			}
		}

	label Newest, Oldest;
	strcpy(Newest, ltoac(cfg.newest));
	strcpy(Oldest, ltoac(cfg.oldest));
	rmPrintf(getmsg(725), ltoac(cfg.newest - cfg.oldest + 1), Oldest,
			Newest);
	doCR();

	if ((cfg.mtoldest - cfg.oldest) > 0 && TI()CurrentUser->IsAide())
		{
		rmPrintf(getmsg(726), ltoac(cfg.mtoldest - cfg.oldest));

		if (TI()MRO.Verbose)
			{
			rmPrintf(getmsg(1487));
			}
		else
			{
			doCR();
			}
		}

	// if oldest message on system is 1, or 1st message in table is located
	// at very beginning of message file, then use current position to
	// compute average message length.

	if (cfg.oldest == 1L || (getLocation(0) == 0))
		{
		work = cfg.catLoc;
		percentfull = (int) (((cfg.catLoc * 25L ) / ((long)cfg.messagek * 256L)));
		}
	else
		{
		work = ((long) cfg.messagek * 1024l);
		percentfull = 100;
		}

	average = (work) / (cfg.newest - cfg.oldest + 1);

	if (TI()MRO.Verbose)
		{
		label Public, Private, Networked, Local, Moderated, Censored;
		strcpy(Public, ltoac(public_mess));
		strcpy(Private, ltoac(private_mess));
		strcpy(Networked, ltoac(networked_mess));
		strcpy(Local, ltoac(local_mess));
		strcpy(Moderated, ltoac(moderated_mess));
		strcpy(Censored, ltoac(censored_mess));

		rmPrintf(getmsg(727), Public, Private, Networked, Local, Moderated,
				Censored, ltoac(massemail_mess));

		if (!TI()CurrentUser->IsAide())
			{
			rmPrintf(getmsg(728));
			}

		rmPrintf(getmsg(729), ltoac(group_mess), cfg.Lgroup_nym);

		if (TI()CurrentUser->IsAide())
			{
			label Copies;
			strcpy(Copies, ltoac(copies_mess));

			rmPrintf(getmsg(730), Copies, ltoac(problem_mess),
					cfg.Luser_nym);
			}

		doCR();

		if (cfg.messagek > 10*1024)
			{
			long Megabytes = cfg.messagek / 1024;

			long Thousandths = ((cfg.messagek % 1024) * 1000) / 1024;

			// Here we are rouding to the nearest hundredth.
			if (Thousandths % 10 > 4)
				{
				Thousandths += 10;
				}

			if (Thousandths > 1000)
				{
				Megabytes++;
				}

			// Now this is really hundredths.
			label MessageM;

			sprintf(MessageM, getmsg(1488), ltoac(Megabytes),
					Thousandths / 10);

			rmPrintf(getmsg(731), MessageM, 'M', cfg.Lmsg_nym,
					ltoac(cfg.nmessages), cfg.Lmsg_nym);
			}
		else
			{
			label MessageK;

			strcpy(MessageK, ltoac(cfg.messagek));

			rmPrintf(getmsg(731), MessageK, 'K', cfg.Lmsg_nym,
					ltoac(cfg.nmessages), cfg.Lmsg_nym);
			}

		if (percentfull < 100)
			{
			rmPrintf(getmsg(732), cfg.Lmsg_nym, percentfull);
			}

		doCR();
		}

	rmPrintf(getmsg(733), ltoac(average), cfg.Lmsg_nym);
	doCR();

	if (TI()MRO.Verbose)
		{
		label Entered;
		strcpy(Entered, ltoac(TI()MS.Entered));

		rmPrintf(getmsg(734), Entered, ltoac(TI()MS.Read),
				(TI()loggedIn) ? ns : getmsg(735));
		doCR();
		}

	// System and debugging
	TI()UserControl.ResetOutParagraph();

	if (TI()MRO.Verbose)
		{
		doCR();
		rmPrintf(getmsg(736));
		doCR();

		if (OSType == OS_OS2)
			{
			rmPrintf(getmsg(10), OSMajor, OSMinor);
			doCR();
			}
		else
			{
			rmPrintf(getmsg(737), _osmajor, _osminor);
			doCR();

			switch (OSType)
				{
				case OS_DV:
					{
					rmPrintf(getmsg(1680), OSMajor, OSMinor);
					doCR();
					break;
					}

				case OS_WINS:
				case OS_WIN3:
					{
					rmPrintf(getmsg(1681), OSMajor, OSMinor,
							OSType == OS_WINS ? getmsg(1682) : getmsg(1683));
					doCR();
					break;
					}
				}
			}

#ifdef WINCIT
		rmPrintf(getmsg(1127), GetVersion() & 255,
				GetVersion() >> 8);
		doCR();
#endif

#ifndef WINCIT
		if (vdDesc[0])
			{
			rmPrintf(getmsg(557), getmsg(597), vdDesc);
			doCR();
			}

		if (cdDesc[0])
			{
			rmPrintf(getmsg(557), getmsg(600), cdDesc);
			doCR();
			}

		if (kdDesc[0])
			{
			rmPrintf(getmsg(557), getmsg(601), kdDesc);
			doCR();
			}

		if (sdDesc[0])
			{
			rmPrintf(getmsg(557), getmsg(602), sdDesc);
			doCR();
			}
#endif

//////////////////////////////////////////////////////////////
#include "config.h"     // Just for fileHandlesLeft
		rmPrintf(getmsg(1292), fileHandlesLeft());
		doCR();
//////////////////////////////////////////////////////////////

#ifdef MSC
		r.h.ah = 0x48;	 // allocate memory
		r.h.al = 0;
		r.x.bx = 0xffff; // ask for an impossible amount

		intdos(&r, &r);

		rmPrintf(getmsg(738), _bios_memsize());
		rmPrintf(getmsg(739), ltoac((long)r.x.bx*16L));
		doCR();
#else
#ifdef WINCIT
		rmPrintf(getmsg(1128), ltoac(GetFreeSpace(0)));
		doCR();
		rmPrintf(getmsg(1129), ltoac(GlobalCompact(0)));
		doCR();
#else
		rmPrintf(getmsg(738), biosmemory());
		rmPrintf(getmsg(739), ltoac(farcoreleft()));
		doCR();
#endif
#endif

#ifndef WINCIT
		if (cmdLine[0])
			{
			rmPrintf(getmsg(476), cmdLine);
			doCR();
			}
#endif

#if DEBUGMEM
		if (debug)
			{
			if (heapcheck() > 0)
				{
				struct heapinfo hi;
				hi.ptr = NULL;

				if (heapwalk(&hi) != _HEAPEMPTY)
					{
enum donetype
	{
	NOT_DONE_YET, LAST_TIME_THROUGH, WE_ARE_NOW_DONE
	} done;
					done = NOT_DONE_YET;

					do
						{
#include "extedit.h"

typedef struct rlmCWL //rlm.cpp
	{
	struct rlmCWL *next;
	CITWINDOW *wnd;
	Bool (*h)(EVENT, long, int, CITWINDOW *);
	} rlmCitWindowList;

#include "huf.h"
#include "mdmreslt.h"

typedef enum //lock.cpp
	{
	LT_ROOM,	LT_HALL,	LT_USER,	LT_MSG
	} ltypes;

typedef struct locks //lock.cpp
	{
	struct locks *next;
	ltypes lockType;
	long lockNum;
	uint key;
	} locks;

#ifdef AUXMEM
						extern auxTabList	*freeList;
						extern int LinesPerPage;
#endif
						extern char 		*scrollBuf;
						extern Bool 		*clipArray;
						extern editors		*extEditorList;
						extern editors		*autoEditList;
						extern hufTree		* cdecl htRoot;
						extern discardable	*htData;
						extern locks		*recLocks;
						extern rlmCitWindowList *rcwl;

						extern discardable *ScriptDData;

						if (hi.ptr)
							{
							rmPrintf(" heapblock %p is %-5ld bytes long and is %s",
									hi.ptr, hi.size,
									hi.in_use ? "in use" : "free");
							}
						else
							{
							rmPrintf(" NULL ");
							}

						if (FP_SEG(hi.ptr) <= _OvrHeapOrg &&
							FP_SEG(hi.ptr) + hi.size / 16 > _OvrHeapOrg)
							{
							rmPrintf(" (Overlay Buffer)");
							}

#define CheckPointer(p) if (CloseEnough(hi.ptr, (void *) (p))) { rmPrintf(" (" #p ")"); }
#define CheckPointerLL(p) CheckLLPtr(p, hi.ptr, #p)
#define CheckPointerDiscardable(p) CheckDiscardablePtr(p, hi.ptr, #p)
#define CheckPointerRLMList(p) CheckRLMListPtr(p, hi.ptr, #p)
#define CheckPointerAuxmem(p) CheckAuxmemPtr(p, hi.ptr, #p)
#define CheckPointerArray(p,i) CheckArrayPtr(p, hi.ptr, #p, i)

						CheckPointer(environ);

						if (environ)
							{
							int i;

							for (i = 0; i < _envSize && environ[i]; i++)
								{
								const void *Environ = environ[i];
								CheckPointerArray(Environ, i);
								}
							}

#ifdef AUXMEM
extern auxTabList *freeBlock;
						CheckPointerAuxmem(freeBlock);	//auxtab
						CheckPointerAuxmem(freeList);	//auxtab
#endif
						CheckPointerLL(ResultCodes);
						CheckPointerLL(Cron.GetEventNum(1));

						CheckPointerLL(allWindows); 	//cwindows
						CheckPointer(getFocus());		//cwindows
						CheckPointer(clipArray);		//cwindows
						CheckPointerLL(extEditorList);	//extedit
						CheckPointerLL(autoEditList);	//extedit
						CheckPointer(GroupData);		//group
						if (GroupData)
							{
							const void * GroupDataPtr =
									GroupData->GetPointer();
							CheckPointer(GroupDataPtr); //group
							}
						CheckPointer(htRoot);			//huf
						CheckPointerDiscardable(htData);//huf
						CheckPointerLL(recLocks);		//lock
						CheckPointer(LogTab);			//log
						if (LogTab)
							{
							const void * LogTabPtr =
									LogTab->GetPointer();
							CheckPointer(LogTabPtr);
							}
						CheckPointerLL(rcwl);			//rlm
						CheckPointer(roomPos);			//room

						CheckPointer(CurScript);					//script

						if (CurScript)
							{
							CheckPointerLL(CurScript->gvBase);
							CheckPointerLL(CurScript->sfBase);
							CheckPointerLL(CurScript->callList);
							CheckPointerLL(CurScript->curCmd);
							CheckPointerLL(CurScript->curFunc);
							CheckPointerLL(CurScript->dupCheck);

							for (int i = 0;
									i < sizeof(CurScript->UsrFile) / sizeof(*CurScript->UsrFile);
									i++)
								{
								if (CurScript->UsrFile[i])
									{
									const void *scrUsrFileBuffer =
											(CurScript->UsrFile[i])->buffer;
									CheckPointerArray(scrUsrFileBuffer, i);
									}
								}

							CheckPointer(CurScript->UsrMsg);
							CheckPointer(CurScript->UsrUsr);
							}

						CheckPointerDiscardable(ScriptDData);//script

						CheckPointer(comDriver);
						CheckPointer(kbdDriver);
						CheckPointer(vidDriver);
						CheckPointer(sndDriver);

						CheckPointerLL(extProtList);
						CheckPointerLL(extDoorList);
						CheckPointerLL(censorList);
						CheckPointerLL(replaceList);
						CheckPointerLL(userAppList);
						CheckPointerLL(holidayList);
						CheckPointerLL(eventList);
						CheckPointerLL(intEventList);
						CheckPointerLL(arcList);

#ifdef AUXMEM
						if (LinesPerPage)
							{
							auxTabList *sbList = (auxTabList *)scrollBuf;
							CheckPointerAuxmem(sbList);
							}
						else
							{
							CheckPointer(scrollBuf);
							}
#else
						CheckPointer(scrollBuf);
#endif

						CheckPointer(msgfl);
						CheckPointer(AccountingData);
						CheckPointer(HallData);
						if (HallData)
							{
							CheckPointer(HallData->GetPointer());

							if (HallData->GetPointer())
								{
								h_slot i;

								for (i = 0; i < cfg.maxhalls; i++)
									{
									const void *HallEntry2 =
										HallData->GetPointer()[i]
										.HallEntry2::GetPointer();
									const void *HallEntry3 =
										HallData->GetPointer()[i]
										.HallEntry3::GetPointer();
									CheckPointerArray(HallEntry2, i);
									CheckPointerArray(HallEntry3, i);
									}
								}
							}
						CheckPointer(borders);
						CheckPointer(encbuf);
#ifdef AUXMEM
						CheckPointer(FirstMessageInRoom);
						CheckPointer(LastMessageInRoom);

						CheckPointerAuxmem(mtList);
						CheckPointerAuxmem(rtList);
						if (TI()talleyInfo)
							{
							// icky, huh? i'll work on it.
							// NOT!
							auxTabList *tbList =
							((talleyTaskInfo *) (TI()talleyInfo))->tbList;
							CheckPointerAuxmem(tbList);
							}
#else
						CheckPointer(msgTab_mtmsgflags);
						CheckPointer(msgTab_mtmsgLocLO);
						CheckPointer(msgTab_mtmsgLocHI);
						CheckPointer(msgTab_mtroomno);
						CheckPointer(msgTab_mttohash);
						CheckPointer(msgTab_mtauthhash);
						CheckPointer(msgTab_mtomesg);
						CheckPointer(roomTab);
#endif

#ifdef MULTI
						CheckPointer(ti);
#endif
						CheckPointer(TI()MS.AbortedMessage);
//						CheckPointerLL(recLocks);
//						CheckPointerLL(firstUnk);
						CheckPointer(statList);
						CheckPointerDiscardable(msgs);
						CheckPointerDiscardable(netmsgs);
						CheckPointerDiscardable(sysmsgs);
						CheckPointerDiscardable(cfgmsgs);
						CheckPointerDiscardable(ecmsgs);
						CheckPointerDiscardable(fsmsgs);
						CheckPointerDiscardable(cwmsgs);
						CheckPointerDiscardable(zmmsgs);
						CheckPointerDiscardable(dbmsgs);
						CheckPointerDiscardable(dateInfo);
						CheckPointer(dgLogiScreen);
						CheckPointerRLMList(RLMmod);
						CheckPointerLL(RLMdo);

						CheckPointer(TI()CurrentUserAccount);
						CheckPointer(TI()node);
						CheckPointer(TI()CurrentUser);
						CheckPointerLL(TI()fileList);
						CheckPointer(TI()roomInfo);
						CheckPointer(TI()talleyInfo);
						CheckPointer(TI()logiScreen);
						CheckPointer(TI()saveBuffer);
						CheckPointerLL(TI()MS.MsgList);
						CheckPointer(TI()AideMsg);

						CheckPointer(trapfl->buffer);
						CheckPointer(roomfl->buffer);
						CheckPointer(stdin->buffer);
						CheckPointer(stdout->buffer);
						CheckPointer(stderr->buffer);
						CheckPointer(stdaux->buffer);
						CheckPointer(stdprn->buffer);
						CheckPointer(journalfl->buffer);
						CheckPointer(TI()OC.PrintFile->buffer);
						CheckPointer(msgfl->buffer.buffer);

						if (TI()CurrentUser)
							{
							const void *LogEntry2 =
									TI()CurrentUser->LogEntry2::GetPointer();
							const void *LogEntry3 =
									TI()CurrentUser->LogEntry3::GetPointer();
							const void *LogEntry4 =
									TI()CurrentUser->LogEntry4::GetPointer();
							const void *LogEntry5 =
									TI()CurrentUser->LogEntry5::GetPointer();
							const void *LogEntry6 =
									TI()CurrentUser->LogEntry6::GetPointer();

							const void *LogExt1 =
									TI()CurrentUser->LogExtensions::GetPointer1();
							const void *LogExt2 =
									TI()CurrentUser->LogExtensions::GetPointer2();
							const void *LogExt3 =
									TI()CurrentUser->LogExtensions::GetPointer3();
							const void *LogExt4 =
									TI()CurrentUser->LogExtensions::GetPointer4();
							const void *LogExt5 =
									TI()CurrentUser->LogExtensions::GetPointer5();
							const void *LogExt6 =
									TI()CurrentUser->LogExtensions::GetPointer6();
							const void *LogExt7 =
									TI()CurrentUser->LogExtensions::GetPointer7();
							const void *LogExt8 =
									TI()CurrentUser->LogExtensions::GetPointer8();
							const void *LogExt9 =
									TI()CurrentUser->LogExtensions::GetPointer9();
							const void *LogExt10 =
									TI()CurrentUser->LogExtensions::GetPointer10();
							const void *LogExt11 =
									TI()CurrentUser->LogExtensions::GetPointer11();

							CheckPointer(LogEntry2);
							CheckPointer(LogEntry3);
							CheckPointer(LogEntry4);
							CheckPointer(LogEntry5);
							CheckPointer(LogEntry6);

							CheckPointerLL(LogExt1);
							CheckPointerLL(LogExt2);
							CheckPointerLL(LogExt3);
							CheckPointerLL(LogExt4);
							CheckPointerLL(LogExt5);
							CheckPointerLL(LogExt6);
							CheckPointer(LogExt7);
							CheckPointer(LogExt8);
							CheckPointer(LogExt9);
							CheckPointerLL(LogExt10);
							CheckPointerLL(LogExt11);
							}

						if (TI()node)
							{
							CheckPointerLL(TI()node->GetMappedGroups());
							}

						CheckHufPointer(hi.ptr);

						doCR();

						if (done == LAST_TIME_THROUGH)
							{
							done = WE_ARE_NOW_DONE;
							}

						if (heapwalk(&hi) == _HEAPEND)
							{
							hi.ptr = NULL;
							done = LAST_TIME_THROUGH;
							}
						} while ((done != WE_ARE_NOW_DONE) &&
								(TI()UserControl.GetOutFlag() != OUTSKIP));
					}
				else
					{
					rmPrintf(getmsg(743));
					doCR();
					}
				}
			else
				{
				rmPrintf(getmsg(744));
				doCR();
				}
			}
#endif

		strcpy(dtstr, ltoac(TI()SerialPort.GetReceived()));
		rmPrintf(getmsg(745), ltoac(TI()SerialPort.GetTransmitted()), dtstr);
		doCR();

		if (SerialPort.HaveCarrier())
			{
			rmPrintf(getmsg(746), connectbauds[TI()ModemSpeed],
					bauds[TI()SerialPort.GetSpeed()],
					TI()UsingCompression ? ns : getmsg(1603),
					TI()UsingCorrection ? ns : getmsg(1603));
			}
		else
			{
			rmPrintf(getmsg(747), bauds[TI()SerialPort.GetSpeed()]);
			}
		doCR();

		ourtimeout = (TI()loggedIn)
							? ((onConsole) ? cfg.consoletimeout : cfg.timeout)
							: cfg.unlogtimeout;
		rmPrintf(getmsg(1293), ltoac(ourtimeout), ourtimeout == 1 ? ns : justs);
		doCR();
		}
	}

#ifndef WINCIT
// i am not sure if this is the best place for this function,
// overlay-wise. i want to keep it in an overlay because it will not
// be used often. But, it will be called often when it is in use, so i
// want to put it in a small or well used overlay. whatever.
// gee, how about its own overlay?!?

void showStatScreen(void)
	{
	char buf[100], buf2[20];
	int i, j;
	static lDowhat = DUNO;

	char **dowhatmsgs = (char **) msgs->next->aux;

	sprintf(buf, getmsg(1294), programName, version);
	statDisp(0, 1, cfg.wattr, buf);
	statDisp(1, 1, cfg.wattr, getmsg(905));

	strftime(buf, 30, getmsg(1295), 0l);
	statDisp(0, 32, cfg.attr, buf);

	sprintf(buf, getmsg(906), diffstamp(uptimestamp));
	statDisp(0, 54, cfg.attr, buf);

	sprintf(buf, getmsg(907), deansi(cfg.Uuser_nym),
			TI()loggedIn ? TI()CurrentUser->GetName() : getmsg(100));
	statDisp(3, 1, cfg.attr, buf);


	if (TI()DoWhat != PROMPT)
		{
		lDowhat = TI()DoWhat;
		}

	sprintf(buf, getmsg(1296), dowhatmsgs[lDowhat]);
	statDisp(3, 40, cfg.attr, buf);

	if (TI()loggedIn)
		{
		sprintf(buf, getmsg(908), IsRoomInuse(TI()thisRoom) ?
				deansi(GetRoomName(TI()thisRoom)) : getmsg(1289));
		statDisp(4, 1, cfg.attr, buf);
		sprintf(buf, getmsg(909), diffstamp(TI()logtimestamp));
		statDisp(4, 40, cfg.attr, buf);
		}
	else
		{
		sprintf(buf, getmsg(910), IsRoomInuse(TI()thisRoom) ?
				deansi(GetRoomName(TI()thisRoom)) : getmsg(1289));
		statDisp(4, 1, cfg.attr, buf);
		}

	if (TI()loggedIn && cfg.accounting && TI()CurrentUser->IsAccounting())
		{
		if (TI()CurrentUserAccount->IsSpecialTime())
			{
			statDisp(5, 40, cfg.attr, getmsg(911));
			}
		else
			{
			const long C = TI()CurrentUser->GetCredits() / 60;

			sprintf(buf, getmsg(1297), deansi((C == 1) ?
					cfg.Ucredit_nym : cfg.Ucredits_nym), C);
			statDisp(5, 40, cfg.attr, buf);
			}

		sprintf(buf, getmsg(907), cfg.Uhall_nym,
				deansi(HallData->GetEntry(TI()thisHall)->GetName()));
		}
	else
		{
		sprintf(buf, getmsg(913), cfg.Uhall_nym,
				deansi((TI()CurrentUser->IsUsePersonalHall()) ?
				getmsg(1595) : HallData->GetEntry(TI()thisHall)->GetName()));
		}
	statDisp(5, 1, cfg.attr, buf);

	if (TI()loggedIn)
		{
		sprintf(buf, getmsg(1298), getmsg(918), TI()SerialPort.GetReceived(),
				getmsg(914), TI()MS.Entered, getmsg(916),
				TI()CurrentUser->GetPoopcount());
		}
	else
		{
		sprintf(buf, getmsg(1299), getmsg(918), TI()SerialPort.GetReceived(),
				getmsg(914), TI()MS.Entered);
		}
	statDisp(7, 1, cfg.attr, buf);

	sprintf(buf, getmsg(1300), getmsg(917), TI()SerialPort.GetTransmitted(),
			getmsg(915), TI()MS.Read);

	statDisp(8, 1, cfg.attr, buf);

	sprintf(buf, getmsg(919), ltoac(farcoreleft()));
	statDisp(10, 1, cfg.attr, buf);

#ifdef AUXMEM
	i = 22;
	if (xmssize)
		{
		sprintf(buf, getmsg(1301), getmsg(546), ltoac(xmssize));
		statDisp(10, i, cfg.attr, buf);
		i += 19;
		}

	if (emssize)
		{
		sprintf(buf, getmsg(1301), getmsg(547),
				ltoac(emssize * (AUXPAGESIZE / 1024)));
		statDisp(10, i, cfg.attr, buf);
		i += 19;
		}

	if (vrtpags)
		{
		sprintf(buf, getmsg(1301), getmsg(548),
				ltoac(vrtpags * (AUXPAGESIZE / 1024)));
		statDisp(10, i, cfg.attr, buf);
		}
#endif

	if (SerialPort.HaveCarrier())
		{
		sprintf(buf, getmsg(920), connectbauds[TI()ModemSpeed],
				bauds[TI()SerialPort.GetSpeed()]);
		if (TI()UsingCorrection)
			{
			strcat(buf, getmsg(1701));
			}
		if (TI()UsingCompression)
			{
			strcat(buf, getmsg(1702));
			}
		}
	else
		{
		sprintf(buf, getmsg(921), bauds[TI()SerialPort.GetSpeed()]);
		}

	statDisp(11, 1, cfg.attr, buf);

	if (updateStatus)
		{
		sprintf(buf, getmsg(1302), getmsg(922), getmsg(923));
		statDisp(13, 1, cfg.attr, buf);

		for (i = 0, j = sl_end; j != sl_start && i < scrollpos - 13; i++)
			{
			j--;
			if (j < 0)
				{
				j = cfg.statnum - 1;
				}
			}

		for (i = 14; j != sl_end; j = ++j % cfg.statnum)
			{
			strftime(buf2, 19, getmsg(1303), statList[j].theTime);
			sprintf(buf, getmsg(1302), deansi(statList[j].theStatus), buf2);
			statDisp(i++, 1, cfg.attr, buf);
			}

		updateStatus = FALSE;
		}
	}
#endif
