// --------------------------------------------------------------------------
// Citadel: Room3.CPP
//
// This should be a temporary file.

#include "ctdl.h"
#pragma hdrstop

#include "room.h"
#include "roompriv.h"
#include "tallybuf.h"
#include "libovl.h"
#include "log.h"
#include "group.h"
#include "msg.h"
#include "hall.h"
#include "blurbs.h"
#include "miscovl.h"
#include "term.h"


// --------------------------------------------------------------------------
// Contents
//
// listRooms()		lists known rooms
// ShowRoomStatus() Shows the status of a room...
// stepRoom()		goes to next or previous room
// gotoRoom()		handles "g(oto)" command for menu
// unGotoRoom()


void ListAllRooms(Bool ThisHallOnly, Bool CanGetToRoom, Bool ShowNetID)
	{
	Bool *AccessArray = NULL;

	if (CanGetToRoom)
		{
		AccessArray = new Bool[cfg.maxrooms];

		if (AccessArray)
			{
			theOtherAlgorithm(AccessArray, TI()thisHall, FALSE);
			}
		else
			{
			mPrintf(getmsg(188), getmsg(1308));
			return;
			}
		}

	CRmPrintfCR(getmsg(962), cfg.Urooms_nym);

	prtList(LIST_START);

	for (r_slot RoomSlot = 0; RoomSlot < cfg.maxrooms; RoomSlot++)
		{
		const r_slot R = RoomPosTableToRoom(RoomSlot);
		if (TI()CurrentUser->CanAccessRoom(R, ThisHallOnly, FALSE))
			{
			if (!CanGetToRoom || AccessArray[R])
				{
				if (ShowNetID)
					{
					if (*GetRoomNetID(R))
						{
						prtList(GetRoomNetID(R));
						}
					}
				else
					{
					prtList(GetRoomName(R));
					}
				}
			}
		}

	prtList(LIST_END);

	delete [] AccessArray;
	}

static Bool ShouldShowRoom(ulong what, r_slot R, g_slot G, const char *Keyword)
	{
	label RoomName;

	if (*Keyword)
		{
		CopyStringToBuffer(RoomName, GetRoomName(R), sizeof(RoomName) - 1);
		stripansi(RoomName);
		}

	return (
			(!(what & K_APPLIC) || getRoomTab(R)->rtflags.IsApplication())
		&&
			(!(what & K_ANON) || getRoomTab(R)->rtflags.IsAnonymous())
		&&
			(!(what & K_PERM) || getRoomTab(R)->rtflags.IsPermanent())
		&&
			(!(what & K_DIR) || getRoomTab(R)->rtflags.IsMsDOSdir())
		&&
			(
			!(what & K_GROUP) ||
				(
				G == CERROR ?
					(
					getRoomTab(R)->rtflags.IsGroupOnly()
					)
				:
					(
					IsRoomGroupOnly(R) ?
						(
						IsRoomMultiGroup(R) ?
							(
							IsInBooleanExpression(GetRoomGroupExpression(R), G)
							)
						:
							(
							GetRoomGroupNumber(R) == G
							)
						)
					:
						(
						FALSE
						)
					)
				) 
			)
		&&
			(!(what & K_LOCAL) || !getRoomTab(R)->rtflags.IsShared())
		&& 
			(!(what & K_SHARED) || getRoomTab(R)->rtflags.IsShared())
		&&
			(!(what & K_THALL) || roomonlyinthishall(R))
		&&
			(!(what & K_NOMSGS) || !GetRoomMessages(R))
		&&
			(!(what & K_BIO) || getRoomTab(R)->rtflags.IsBIO())
		&&
			(!(what & K_HIDDEN) || !getRoomTab(R)->rtflags.IsPublic())
		&&
			(!(what & K_WINDOWS) || iswindow(R))
		&&
			(!(what & K_KEYWORD) || u_match(RoomName, Keyword))
		);
	}


// --------------------------------------------------------------------------
// listRooms(): Lists known rooms.
//
// Input:
//	ulong what: Bitwise ORed (|) flags, defined in CTDL.H:
//		K_APPLIC	0x00000001l 			K_ANON		0x00000002l
//		K_PERM		0x00000004l 			K_DIR		0x00000008l
//		K_GROUP 	0x00000010l 			K_LOCAL 	0x00000020l
//		K_NEW		0x00000040l 			K_OLD		0x00000080l
//		K_MAIL		0x00000100l 			K_SHARED	0x00000200l
//		K_THALL 	0x00000400l 			K_WINDOWS	0x00000800l
//		K_EXCLUDE	0x00001000l 			K_NUM		0x00002000l
//		K_NOMSGS	0x00004000l 			K_BIO		0x00008000l
//		K_HIDDEN	0x00010000l 			K_KEYWORD	0x00020000l

void listRooms(ulong what)
	{
	int i;

	SetDoWhat(KNOWNROOMS);

	TI()MRO.Reverse = FALSE;

	if ((what & K_THALL) && TI()CurrentUser->IsUsePersonalHall())
		{
		CRmPrintfCR(getmsg(859), cfg.Lhall_nym, cfg.Lhall_nym);
		return;
		}

	g_slot groupslot;

	if (what & K_GROUP)
		{
		label groupname;

		if (!GetStringWithBlurb(cfg.Lgroup_nym, groupname,
				LABELSIZE, ns, B_KNOWNGRP))
			{
			return;
			}

		normalizeString(groupname);

		if (*groupname)
			{
			groupslot = FindGroupByPartialName(groupname, FALSE);

			if (groupslot != CERROR &&
					!TI()CurrentUser->IsInGroup(groupslot))
				{
				groupslot = CERROR;
				}

			if (groupslot != CERROR)
				{
				CRmPrintfCR(getmsg(385), cfg.Uroom_nym, cfg.Lgroup_nym,
						GroupData->GetEntry(groupslot)->GetName());
				}
			else
				{
				CRmPrintfCR(getmsg(947), cfg.Lgroup_nym);
				return;
				}
			}
		else
			{
			groupslot = CERROR;
			CRmPrintfCR(getmsg(1600), cfg.Uroom_nym, cfg.Lgroup_nym);
			}
		}


	label Keyword;
	if (what & K_KEYWORD)
		{
		if (!GetStringWithBlurb(getmsg(1703), Keyword, sizeof(Keyword) - 1,
				ns, B_KNWNKYWD))
			{
			return;
			}

		normalizeString(Keyword);

		if (!*Keyword)
			{
			what &= ~K_KEYWORD;
			}
		}

	if (!(what & K_KEYWORD))
		{
		*Keyword = 0;
		}


	TI()UserControl.SetOutFlag(OUTOK);

	TI()showdir 	= FALSE;
	TI()showhidden	= FALSE;
	TI()showbio 	= FALSE;
	TI()showgroup	= FALSE;

	const Bool ShowNum = !!(what & K_NUM);

	// NEW rooms
	if (	// Normal Known, or .KN
			(!what || what & K_NEW) &&

			// and not .KX (we don't care if they are new)
			!(what & K_EXCLUDE) &&

			// and not .K0 (if there are no messages, none will be new)
			!(what & K_NOMSGS) &&

			// and not .KM (we list .KM all at once)
			!(what & K_MAIL) &&

			// and not .KW (we list .KW all at once)
			!(what & K_WINDOWS)
		)
		{
		char wow[64];

		if (TI()CurrentUser->IsUsePersonalHall())
			{
			sprintf(wow, getmsg(858), cfg.Lhall_nym);
			}
		else
			{
			wow[0] = 0;
			}

		CRmPrintfCR(getmsg(386), cfg.Urooms_nym, cfg.Lmsgs_nym,
				*wow ? wow : HallData->GetEntry(TI()thisHall)->GetName());

		if (!TI()MRO.Verbose && !TI()CurrentUser->IsWideRoom())
			{
			prtList(LIST_START);
			}

		for (i = 0; i < cfg.maxrooms &&
				(TI()UserControl.GetOutFlag() != OUTSKIP); i++)
			{
			const r_slot R = RoomPosTableToRoom(i);

			if (TI()CurrentUser->CanAccessRoom(R, TRUE, FALSE) &&
					!TI()CurrentUser->IsRoomExcluded(R) &&
					GetRoomNewMessages(R)
					)
				{
				if (ShouldShowRoom(what, R, groupslot, Keyword))
					{
					printroomVer(R, ShowNum);
					}
				}
			}

		if (!TI()MRO.Verbose && !TI()CurrentUser->IsWideRoom())
			{
			if (!ShowNum)
				{
				prtList(LIST_END);
				}
			}
		else
			{
			doCR();
			}
		}

	if (TI()UserControl.GetOutFlag() == OUTSKIP)
		{
		return;
		}

	if (TI()UserControl.GetOutFlag() == OUTPARAGRAPH)
		{
		TI()UserControl.SetOutFlag(OUTOK);
		}

	// new MAIL rooms
	if (
			// Normal K or .KN
			(!what || what & K_NEW) ||

			// or .KM, but not .KOM
			(
				(what & K_MAIL) &&
				!(what & K_OLD)
			)
		)
		{
		Bool FirstTime = TRUE;

		for (i = 0; i < cfg.maxrooms && (TI()UserControl.GetOutFlag() != OUTSKIP); i++)
			{
			const r_slot R = RoomPosTableToRoom(i);
			if (TI()CurrentUser->CanAccessRoom(R, TRUE, FALSE) &&
				IsRoomMail(R)
				)
				{
				if (ShouldShowRoom(what, R, groupslot, Keyword))
					{
					if (FirstTime)
						{
						CRmPrintfCR(getmsg(387), cfg.Lmsgs_nym);

						FirstTime = FALSE;

						if (!TI()MRO.Verbose && !TI()CurrentUser->IsWideRoom())
							{
							prtList(LIST_START);
							}
						}

					printroomVer(R, ShowNum);
					}
				}
			}

		if (!FirstTime)
			{
			if (!TI()MRO.Verbose && !TI()CurrentUser->IsWideRoom())
				{
				if (!ShowNum)
					{
					prtList(LIST_END);
					}
				}
			else
				{
				doCR();
				}
			}
		}

	if (TI()UserControl.GetOutFlag() == OUTSKIP)
		{
		return;
		}

	if (TI()UserControl.GetOutFlag() == OUTPARAGRAPH)
		{
		TI()UserControl.SetOutFlag(OUTOK);
		}

	// old MAIL rooms - only on .KOM
	if ((what & K_MAIL) && (what & K_OLD))
		{
		Bool FirstTime = TRUE;

		for (i = 0; i < cfg.maxrooms && (TI()UserControl.GetOutFlag() != OUTSKIP); i++)
			{
			const r_slot R = RoomPosTableToRoom(i);
			if (TI()CurrentUser->CanAccessRoom(R, TRUE, FALSE) &&
				IsRoomOldMail(R))
				{
				if (ShouldShowRoom(what, R, groupslot, Keyword))
					{
					if (FirstTime)
						{
						CRmPrintfCR(getmsg(933));

						FirstTime = FALSE;
						if (!TI()MRO.Verbose && !TI()CurrentUser->IsWideRoom())
							{
							prtList(LIST_START);
							}
						}

					printroomVer(R, ShowNum);
					}
				}
			}

		if (!FirstTime)
			{
			if (!TI()MRO.Verbose && !TI()CurrentUser->IsWideRoom())
				{
				if (!ShowNum)
					{
					prtList(LIST_END);
					}
				}
			else
				{
				doCR();
				}
			}
		}

	if (TI()UserControl.GetOutFlag() == OUTSKIP)
		{
		return;
		}

	if (TI()UserControl.GetOutFlag() == OUTPARAGRAPH)
		{
		TI()UserControl.SetOutFlag(OUTOK);
		}

	// OLD rooms
	if (
			// Normal K or .KO
			(!what || what & K_OLD) &&

			// and not .KX (all listed at once)
			!(what & K_EXCLUDE) &&

			// and not .KM
			!(what & K_MAIL) &&

			// and not .KW
			!(what & K_WINDOWS))
		{

		// "no unseen messages" or "no messages" if .K0
		CRmPrintfCR((what & K_NOMSGS) ? getmsg(1692) : getmsg(388),
				cfg.Lmsgs_nym);

		if (!TI()MRO.Verbose && !TI()CurrentUser->IsWideRoom())
			{
			prtList(LIST_START);
			}

		for (i = 0; i < cfg.maxrooms && (TI()UserControl.GetOutFlag() != OUTSKIP); i++)
			{
			const r_slot R = RoomPosTableToRoom(i);
			if (TI()CurrentUser->CanAccessRoom(R, TRUE, FALSE) &&
					!TI()CurrentUser->IsRoomExcluded(R) &&
					!GetRoomNewMessages(R))
				{
				if (ShouldShowRoom(what, R, groupslot, Keyword))
					{
					printroomVer(R, ShowNum);
					}
				}
			}

		if (!TI()MRO.Verbose && !TI()CurrentUser->IsWideRoom())
			{
			if (!ShowNum)
				{
				prtList(LIST_END);
				}
			}
		else
			{
			doCR();
			}
		}

	if (TI()UserControl.GetOutFlag() == OUTSKIP)
		{
		return;
		}

	if (TI()UserControl.GetOutFlag() == OUTPARAGRAPH)
		{
		TI()UserControl.SetOutFlag(OUTOK);
		}

	// EXCLUDED rooms - list always unless .KN
	if (what != K_NEW)
		{
		Bool FirstTime = TRUE;

		for (i = 0; i < cfg.maxrooms && (TI()UserControl.GetOutFlag() != OUTSKIP); i++)
			{
			const r_slot R = RoomPosTableToRoom(i);
			if (TI()CurrentUser->CanAccessRoom(R, TRUE, FALSE) &&
					TI()CurrentUser->IsRoomExcluded(R)
					)
				{
				if (	// Normal rules
						ShouldShowRoom(what, R, groupslot, Keyword) &&

						// Plus: Don't list if .KM
						(!(what & K_MAIL) || IsRoomMail(R) || IsRoomOldMail(R))
					)
					{
					if (FirstTime)
						{
						CRmPrintfCR(getmsg(389), cfg.Lrooms_nym);

						FirstTime = FALSE;

						if (!TI()MRO.Verbose && !TI()CurrentUser->IsWideRoom())
							{
							prtList(LIST_START);
							}
						}

					printroomVer(R, ShowNum);
					}
				}
			}

		if (!FirstTime)
			{
			if (!TI()MRO.Verbose && !TI()CurrentUser->IsWideRoom())
				{
				if (!ShowNum)
					{
					prtList(LIST_END);
					}
				}
			else
				{
				doCR();
				}
			}
		}

	if (TI()UserControl.GetOutFlag() == OUTSKIP)
		{
		return;
		}

	if (TI()UserControl.GetOutFlag() == OUTPARAGRAPH)
		{
		TI()UserControl.SetOutFlag(OUTOK);
		}

	// WINDOWS
	if (what & K_WINDOWS && !(what & K_EXCLUDE))
		{
		Bool FirstTime = TRUE;

		if (!TI()MRO.Verbose && !TI()CurrentUser->IsWideRoom())
			{
			prtList(LIST_START);
			}

		for (i = 0; i < cfg.maxrooms && (TI()UserControl.GetOutFlag() != OUTSKIP); i++)
			{
			const r_slot R = RoomPosTableToRoom(i);
			if (TI()CurrentUser->CanAccessRoom(R, TRUE, FALSE) &&
					!TI()CurrentUser->IsRoomExcluded(R)
					)
				{
				if (ShouldShowRoom(what, R, groupslot, Keyword))
					{
					if (FirstTime)
						{
						CRmPrintfCR(getmsg(390), cfg.Urooms_nym,
								cfg.Lhalls_nym);

						FirstTime = FALSE;

						if (!TI()MRO.Verbose && !TI()CurrentUser->IsWideRoom())
							{
							prtList(LIST_START);
							}
						}

					if (TI()MRO.Verbose)
						{
						char string[LABELSIZE+LABELSIZE];
						mPrintf(getmsg(1457), MakeRoomName(R, string));

						prtList(LIST_START);

						for (int j = 0 ; j < cfg.maxhalls; j++)
							{
							if (
									HallData->GetEntry(j)->IsInuse()

									&&

									HallData->GetEntry(j)->IsWindowedIntoHall(R)

									&&

									TI()CurrentUser->CanAccessHall(j))
								{
								prtList(HallData->GetEntry(j)->GetName());
								}
							}

						prtList(LIST_END);
						}
					else
						{
						TI()MRO.Verbose = FALSE;
						printroomVer(R, FALSE);
						}
					}
				}
			}

		if (!FirstTime && !TI()MRO.Verbose && !TI()CurrentUser->IsWideRoom())
			{
			prtList(LIST_END);
			}
		else
			{
			if (!FirstTime && !TI()MRO.Verbose)
				{
				doCR();
				}
			}
		}

	if (TI()UserControl.GetOutFlag() == OUTSKIP)
		{
		return;
		}

	if (TI()UserControl.GetOutFlag() == OUTPARAGRAPH)
		{
		TI()UserControl.SetOutFlag(OUTOK);
		}

	if (!TI()CurrentUser->IsExpert())
		{
		if (TI()showhidden)
			{
			CRmPrintf(getmsg(391), cfg.Lroom_nym);
			}

		if (TI()showbio)
			{
			CRmPrintf(getmsg(392), cfg.Lroom_nym);
			}

		if (TI()showgroup)
			{
			CRmPrintf(getmsg(393), TI()CurrentUser->IsIBMRoom() ? '\xb3' : ':',
					cfg.Lgroup_nym, cfg.Lroom_nym);
			}

		if (TI()showdir)
			{
			CRmPrintf(getmsg(394), TI()CurrentUser->IsIBMRoom() ? '\xb9' : ']',
					cfg.Lroom_nym);
			}

		if (TI()showhidden || TI()showbio || TI()showgroup || TI()showdir)
			{
			doCR();
			}
		}
	}


// --------------------------------------------------------------------------
// ShowRoomStatus(): Shows the status of a room.

void ShowRoomStatus(void)
	{
	char buff[500];

	SetDoWhat(KNOWNRMINFO);

	TI()UserControl.SetOutFlag(OUTOK);

	CRmPrintf(pctss, cfg.Uhall_nym);
	mPrintf(pcts, HallData->GetEntry(TI()thisHall)->GetName());

	if (HallData->GetEntry(TI()thisHall)->IsOwned())
		{
		if (HallData->GetEntry(TI()thisHall)->IsBoolGroup())
			{
			mPrintf(getmsg(1309));
			}
		else
			{
			mPrintf(getmsg(396), cfg.Lgroup_nym,
					GroupData->GetEntry(HallData->GetEntry(TI()thisHall)->GetGroupNumber())->
					GetName());
			}
		}
	doCR();

	CreateRoomSummary(buff, TI()thisRoom);
	CRmPrintfCR(pcts, buff);

	if (iswindow(TI()thisRoom))
		{
		CRmPrintfCR(getmsg(397), cfg.Lhalls_nym);

		prtList(LIST_START);
		for (h_slot j = 0; j < cfg.maxhalls; j++)
			{
			if (
					HallData->GetEntry(j)->IsInuse()

					&&

					HallData->GetEntry(j)->IsWindowedIntoHall(TI()thisRoom)

					&&

					TI()CurrentUser->CanAccessHall(j))
				{
				prtList(HallData->GetEntry(j)->GetName());
				}
			}
		prtList(LIST_END);
		}

	readhalls();

	dumpRoom(FALSE);
	}


// --------------------------------------------------------------------------
// printroomVer(): displays name of specified room.
//
// Input:
//	r_slot room: Room to display
//	Bool numMess: Should we talk about the number of messages?
//
// Notes:
//	Called by listRooms. Sets global flags.

void printroomVer(r_slot room, Bool numMess)
	{
	char string[LABELSIZE+LABELSIZE];

	MakeRoomName(room, string);

	if (getRoomTab(room)->rtflags.IsGroupOnly())
		{
		TI()showgroup = TRUE;
		}

	if (getRoomTab(room)->rtflags.IsMsDOSdir())
		{
		TI()showdir = TRUE;
		}

	if (!getRoomTab(room)->rtflags.IsPublic())
		{
		TI()showhidden = TRUE;
		}

	if (getRoomTab(room)->rtflags.IsBIO())
		{
		TI()showbio = TRUE;
		}

	if (!TI()MRO.Verbose && !numMess)
		{
		if (TI()CurrentUser->IsWideRoom())
			{
			if (TI()OC.CrtColumn > 3 && TI()OC.CrtColumn < 40)
				{
				while (TI()OC.CrtColumn < 40)
					{
					oChar(' ');
					}
				}
			else
				{
				doCR();
				}

			mPrintf(pcts, string);
			termCap(TERM_NORMAL);

			TI()PLlisted = TRUE;	// for prtList(LIST_END)
			}
		else
			{
			prtList(string);
			}

		}
	else
		{
		aRoom rBuf;

		getRoom(room, &rBuf);

		mPrintf(getmsg(1310), string, 33 - strlen(deansi(string)), ns);

		// this sux, fix it later
		if (!numMess)
			{
			if (*rBuf.netID && rBuf.rbflags.IsShared())
				{
				sprintf(string, getmsg(1458), rBuf.netID);
				mPrintf(getmsg(1459), string);
				}
			}
		/////////////////////////

		if (numMess)
			{
			if (TI()CurrentUser->IsAide())
				{
				mPrintf(getmsg(398), (long) GetRoomTotalMessages(room));
				}

			mPrintf(getmsg(399), (long) GetRoomMessages(room),
					GetRoomMessages(room) == 1 ? cfg.Lmsg_nym : cfg.Lmsgs_nym,
					(long) GetRoomNewMessages(room));

			if (GetRoomNewMessages(room) && IsRoomMail(room))
				{
				mPrintf(getmsg(400));
				}

			doCR();

			if (*rBuf.descript && TI()MRO.Verbose)
				{
				mPrintf(getmsg(1460));
				}
			}

		if (TI()MRO.Verbose)
			{
			if (*rBuf.descript)
				{
				mPrintf(pcts, rBuf.descript);
				}

			if (!numMess || *rBuf.descript)
				{
				doCR();
				}
			}

		if (TI()MRO.Verbose && numMess)
			{
			doCR();
			}
		}
	}


// --------------------------------------------------------------------------
// stepRoom(): Move to an adjacent room.
//
// Input:
//	Bool direction: TRUE for next; FALSE for previous

void stepRoom(Bool direction)
	{
	int i, rp;
	char done = FALSE;

	i = rp = GetRoomPosInTable(TI()thisRoom);

	do
		{
		if (direction)
			{
			if (++i == cfg.maxrooms)
				{
				i = 0;
				}
			}
		else
			{
			if (--i == -1)
				{
				i = cfg.maxrooms - 1;
				}
			}

		if (i == rp || (TI()CurrentUser->CanAccessRoom(RoomPosTableToRoom(i),
				TRUE, FALSE)))
			{
			// how's this for icky? We copy it into our own buffer because
			// gotoRoom calls things that swap out the room table - and
			// thus this pointer starts pointing to another room's name.
			// it looks really cool on-screen, but it is not good in the end.
			//
			// Lobby>> Previous Room: Some other room
			//
			//	No such room: "Another room".
			//
			// Lobby>>

			// for the i == rp case if killed only room in hall.
			if (!IsRoomInuse(RoomPosTableToRoom(i)))
				{
				mPrintfCR(pcts, getRoomTab(RoomPosTableToRoom(LOBBY))->rtname);
				getRoom(LOBBY, roomBuf);
				TI()thisRoom = LOBBY;
				dumpRoom(TRUE);
				}
			else
				{
				label RoomName;
				CopyStringToBuffer(RoomName,
						getRoomTab(RoomPosTableToRoom(i))->rtname, LABELSIZE);

				mPrintfCR(pcts, RoomName);

				gotoRoom(RoomName, FALSE, FALSE, FALSE);
				}

			done = TRUE;
			}
		} while (!done);
	}


// --------------------------------------------------------------------------
// unGotoRoom()

void unGotoRoom(void)
	{
	Bool tryAgain;
	int TryCounter = 0;

	mPrintf(getmsg(927));

	do
		{
		jumpback JB;
		tryAgain = FALSE;

		if (TI()CurrentUser->JumpbackPop(&JB))
			{
			if (JB.room < cfg.maxrooms && JB.room >= 0 &&
					TI()CurrentUser->CanAccessRoom(JB.room, FALSE, FALSE)
// too slow && theAlgorithm(JB.room, TI()thisHall, FALSE)
					)
				{
				if (JB.hall < cfg.maxhalls && JB.hall >= 0 &&
						HallData->GetEntry(JB.hall)->IsInuse() &&
						TI()CurrentUser->CanAccessHall(JB.hall))
					{
					TI()thisHall = JB.hall;
					}

				doBS(TryCounter);

				mPrintfCR(spcts, getRoomTab(JB.room)->rtname);

				TI()thisRoom = JB.room;
				getRoom(TI()thisRoom, roomBuf);
				checkdir();

				SetRoomBypass(TI()thisRoom, JB.bypass);
				TI()CurrentUser->SetRoomNewPointer(TI()thisRoom, JB.newpointer);
				SetRoomNewMessages(TI()thisRoom, JB.newMsgs);

				dumpRoom(TRUE);
				}
			else
				{
				mPrintf(getmsg(1433));
				TryCounter++;
				tryAgain = TRUE;
				}
			}
		else
			{
			doBS(TryCounter + strlen(getmsg(927)));

			mPrintf(getmsg(344));
			}
		} while (tryAgain);
	}


// --------------------------------------------------------------------------
// gotoRoom(): Travel to a new room
//
// Input:
//	const char *roomname: Room name to go to or empty string for next room
//		with new messages.
//	Bool skiproom: TRUE to not update message pointers (Bypass)
//	Bool partial: TRUE to allow roomname to be partial; FALSE for exact
//	Bool mailScan: TRUE to skip to next room with mail. Overrides roomname

void gotoRoom(const char *roomname, Bool skiproom, Bool partial, Bool mailScan)
	{
	int i, j, rp;
	int roomNo = CERROR;
	h_slot oldHall = TI()thisHall;

	rp = GetRoomPosInTable(TI()thisRoom);

	// make sure we can get back
	TI()CurrentUser->SetInRoom(TI()thisRoom, TRUE);

	if (!(*roomname) || mailScan)
		{
		Bool *RoomAccess = NULL;

		if (mailScan)
			{
			RoomAccess = new Bool[cfg.maxrooms];

			if (RoomAccess)
				{
				theOtherAlgorithm(RoomAccess, TI()thisHall, FALSE);
				}
			}

		for (j = rp + 1, i = 0;
				j != rp && roomNo == CERROR && i < cfg.maxrooms; j++, i++)
			{
			if (j == cfg.maxrooms)
				{
				j = 0;
				}

			if (mailScan)
				{
				Bool HasMail;

				if (RoomAccess)
					{
					HasMail = RoomAccess[RoomPosTableToRoom(j)] &&
							IsRoomMail(RoomPosTableToRoom(j));
					}
				else
					{
					// Wasn't enough memory for theOtherAlgorithm... use
					// theAlgorithm.

					HasMail =
						// auxmem or wincit - faster to to theAlgorithm.
						// else, faster to do IsRoomMail
						#if defined(AUXMEM) || defined(WINCIT)
							theAlgorithm(RoomPosTableToRoom(j),
									TI()thisHall, FALSE) &&
									IsRoomMail(RoomPosTableToRoom(j));
						#else
							IsRoomMail(RoomPosTableToRoom(j)) &&
									theAlgorithm(RoomPosTableToRoom(j),
									TI()thisHall, FALSE);
						#endif
					}

				if (HasMail)
					{
					roomNo = RoomPosTableToRoom(j);
					}
				}
			else
				{
				if (TI()CurrentUser->CanAccessRoom(RoomPosTableToRoom(j), TRUE, FALSE) &&
						(GetRoomNewMessages(RoomPosTableToRoom(j))

				// or is it a window?
				|| (iswindow(RoomPosTableToRoom(j)) && j > rp
					&& TI()thisHall != 1 && !(cfg.subhubs == 4)
					&& cfg.subhubs == 2 /* DragCit-style: empties visited */))

				// is it NOT excluded
				&&	(!TI()CurrentUser->IsRoomExcluded(RoomPosTableToRoom(j)) ||
						IsRoomMail(RoomPosTableToRoom(j)))

				// is it NOT bypassed
				&&	(!WasRoomBypassed(RoomPosTableToRoom(j)))

				// we do not come back to current room
				&&	 j != rp
				)
					{
					roomNo = RoomPosTableToRoom(j);
					}
				}
			}

		delete [] RoomAccess;

		if (roomNo == CERROR && !(cfg.subhubs == 4) && cfg.subhubs)
			{
			for (j = rp + 1, i = 0;
				 j != rp && roomNo == CERROR && i < cfg.maxrooms;
				 j++, i++)
				{
				if (j == cfg.maxrooms)
					{
					j = 0;
					}

				if (iswindow(RoomPosTableToRoom(j))

				// can user access this room?
				&& TI()CurrentUser->CanAccessRoom(RoomPosTableToRoom(j), TRUE, FALSE)

				// is it NOT excluded
				&& !TI()CurrentUser->IsRoomExcluded(RoomPosTableToRoom(j))

				// is it NOT bypassed
				&& !WasRoomBypassed(RoomPosTableToRoom(j)))
					{
					roomNo	= RoomPosTableToRoom(j);
					}
				}
			}

		if ((mailScan || (cfg.subhubs == 4)) && roomNo == CERROR)
			{
			roomNo = LOBBY;
			}

		if (roomNo != CERROR)
			{
			mPrintf(pcts, getRoomTab(roomNo)->rtname);
			}
		}
	else
		{
		if (TI()CurrentUser->IsUsePersonalHall())
			{
			r_slot check = RoomExists(roomname);

			if (check != CERROR)
				{
				if (!TI()CurrentUser->CanAccessRoom(check, FALSE, TRUE))
					{
					check = CERROR;
					}
				else
					{
					TI()CurrentUser->SetInRoom(check, TRUE);
					}
				}

			if (check == CERROR && partial)
				{
				check = PartialRoomExists(roomname, TI()thisRoom, TRUE);
				}

			if (check != CERROR &&
					theAlgorithm(check, TI()thisHall, FALSE) &&
					TI()CurrentUser->CanAccessRoom(check, FALSE, FALSE))
				{
				roomNo = check;
				}
			else
				{
				CRmPrintfCR(getmsg(157), cfg.Lroom_nym, roomname);
				}
			}
		else
			{
			r_slot check = RoomExists(roomname);

			if (check != CERROR)
				{
				if (!TI()CurrentUser->CanAccessRoom(check, TRUE, TRUE))
					{
					check = CERROR;
					}
				else
					{
					TI()CurrentUser->SetInRoom(check, TRUE);
					}
				}

			if (check == CERROR && partial)
				{
				check = PartialRoomExists(roomname, TI()thisRoom, FALSE);
				}

			if (check != CERROR && TI()CurrentUser->CanAccessRoom(check, TRUE, FALSE))
				{
				roomNo = check;
				}
			else
				{
				CRmPrintfCR(getmsg(157), cfg.Lroom_nym, roomname);
				}
			}
		}

	if (roomNo != CERROR)
		{
		if (roomNo != TI()thisRoom)
			{
			jumpback jb;

			jb.hall = oldHall;
			jb.room = TI()thisRoom;
			jb.newpointer = TI()CurrentUser->GetRoomNewPointer(TI()thisRoom);
			jb.bypass = WasRoomBypassed(TI()thisRoom);
			jb.newMsgs = GetRoomNewMessages(TI()thisRoom);

			TI()CurrentUser->JumpbackPush(jb);
			}

		// update new pointer for this room
		if (!skiproom)
			{
			SetRoomVisited(TI()thisRoom, TRUE);
			TI()CurrentUser->SetRoomNewPointer(TI()thisRoom, cfg.newest);

			SetRoomHasMail(TI()thisRoom, FALSE);
			SetRoomHasOldMail(TI()thisRoom, FALSE);
			SetRoomNewMessages(TI()thisRoom, 0);
			}

		if ((roomNo != TI()thisRoom) && skiproom && TI()thisRoom != LOBBY)
			{
			SetRoomBypass(TI()thisRoom, TRUE);
			}

		// Get room and display it.
		TI()thisRoom = roomNo;
		getRoom(roomNo, roomBuf);
		checkdir();

		if (!*roomname)
			{
			if (((!(cfg.subhubs == 4) && iswindow(roomNo)) ||
					(cfg.subhubs == 4 && roomNo == LOBBY)) &&
					!TI()CurrentUser->IsUsePersonalHall() &&
					TI()CurrentUser->IsAutoNextHall())
				{
				mPrintf(getmsg(928), cfg.Lhall_nym);
				stephall(1);
				}

			doCR();
			}

		dumpRoom(TRUE);

		if (TI()CurrentUser->IsUsePersonalHall())
			{
			TI()CurrentUser->SetRoomInPersonalHall(TI()thisRoom, TRUE);
			}
		}
	}


Bool IsNetIDCorrupted(const char *NetID)
	{
	if (NetID && *NetID && strlen(NetID) <= LABELSIZE && !hasansi(NetID) &&
			!strchr(NetID, '"'))
		{
		for (int i = 0; NetID[i]; i++)
			{
			if (NetID[i] < 32)
				{
				return (TRUE);
				}
			}

		label Buffer;

		strcpy(Buffer, NetID);
		normalizeString(Buffer);

		return (!SameString(NetID, Buffer));
		}

	return (TRUE);
	}
