// --------------------------------------------------------------------------
// Citadel: DoMisc.CPP
//
// Miscellaneous commands

#include "ctdl.h"
#pragma hdrstop

#include "room.h"
#include "log.h"
#include "viewlog.h"
#include "msg.h"
#include "events.h"
#include "menus.h"
#include "helpfile.h"
#include "blurbs.h"
#include "account.h"
#include "miscovl.h"


// --------------------------------------------------------------------------
// Contents
//
// doChat() 			does it
// doHelp() 			does it
// doIntro()			does it
// doLogout()			does it
// doVolkswagen()		does it
// doSmallChat()		does it
// doY()				does it
// doPoop() 			does it
// doWow()				does it
// doReadBull() 		does it


// --------------------------------------------------------------------------
// doChat()

Bool doChat(Bool ring, Bool doInternal)
	{
	Bool retVal;

	SetDoWhat(DOCHAT);

	TI()chatReq = TRUE;

	if (doInternal)
		{
		mPrintf(getmsg(219));
		}

	trap(getmsg(220), T_CHAT);

	if (cfg.noChat || !TI()CurrentUser->IsChat())
		{
		nochat(FALSE);

		if (TI()chatReq && ((cfg.chatmail == 1) || (cfg.chatmail == 4)))
			{
			Message *Msg = new Message;

			if (Msg)
				{
				msgtosysop(Msg);
				delete Msg;
				}
			else
				{
				mPrintf(getmsg(188), getmsg(620));
				}
			}

		return (FALSE);
		}

	if (TI()whichIO == MODEM)
		{
		char ChatSubject[81];

		if (cfg.chatwhy)
			{
			CRmPrintfCR(getmsg(544));

			oChar('>');
			TI()chatsubject = TRUE;

			getString(ns, ChatSubject, 80, FALSE, ECHO, ns);
			}

		if (ringSysop(ring, doInternal))
			{
			retVal = TRUE;
			}
		else
			{
			if (TI()chatReq && ((cfg.chatmail == 1) || (cfg.chatmail == 4)))
				{
				Message *Msg = new Message;

				if (Msg)
					{
					Msg->SetSubject(ChatSubject);
					msgtosysop(Msg);

					delete Msg;
					}
				else
					{
					mPrintf(getmsg(188), getmsg(620));
					}
				}

			retVal = FALSE;
			}
		}
	else
		{
		if (doInternal)
			{
			chat();
			}

		retVal = TRUE;
		}

	TI()chatsubject = FALSE;
	return (retVal);
	}


// --------------------------------------------------------------------------
// doVolkswagen(): Handles V(olkswagen) command.

void doVolkswagen(Bool moreYet, const char *CmdKeys)
	{
	SetDoWhat(DOVOLKS);

	discardable *d;

	if (!moreYet)
		{
		mPrintf(getmsg(367));
		}
	else if ((d = readData(22)) != NULL)
		{
		Bool HitChars[38];	// A-Z 0-9 ESC CR
		Bool done;
		int theChar, NumBSAble = 0, CmdKeysIndex = 0;

		memset(HitChars, 0, sizeof(HitChars));

		mPrintf(getmsg(406));

		do
			{
			done = FALSE;

			if (CmdKeys && CmdKeys[CmdKeysIndex])
				{
				theChar = toupper(CmdKeys[CmdKeysIndex++]);
				}
			else
				{
				theChar = toupper(iCharNE());
				}

			if (isalnum(theChar) || theChar == ESC || theChar == '\n')
				{
				int hcIndex;

				if (isalpha(theChar))
					{
					hcIndex = theChar - 'A';
					}
				else if (isdigit(theChar))
					{
					hcIndex = theChar - '0' + 26;
					}
				else
					{
					hcIndex = theChar == ESC ? 36 : 37;
					}

				if (!HitChars[hcIndex])
					{
					HitChars[hcIndex] = TRUE;

					for (discardable *Cur = d; Cur;
							Cur = (discardable *) getNextLL(Cur))
						{
						const char **Text = (const char **) Cur->aux;

						if (Text[0][0] == theChar)
							{
							done = !strchr(Text[0], 'c');

							if (strchr(Text[0], 'b'))
								{
								while (NumBSAble--)
									{
									doBS();
									}

								doBS();

								NumBSAble = 0;
								}

							for (int Index = 1; Text[Index][0] != '#'; Index++)
								{
								mPrintf(Text[Index]);

								if (!done)
									{
									NumBSAble += strlen(Text[Index]);
									}
								}

							break;
							}
						}
					}
				}
			else if (theChar == '?')
				{
				oChar('?');
				showMenu(M_VOLKSWAGEN);
				done = TRUE;
				}
			} while (!done);

		if (theChar != '?')
			{
			doCR();
			}

		discardData(d);
		}
	else
		{
		mPrintf(getmsg(188), getmsg(1389));
		}
	}


// --------------------------------------------------------------------------
// doHelp(): Handles H(elp) command.

void doHelp(Bool expand, const char *HelpFile)
	{
	label fileName;

	SetDoWhat(HELPFILES);

	mPrintf(getmsg(263));

	if (!expand)
		{
		doCR();
		dispHlp(H_DOHELP);
		return;
		}

	if (HelpFile)
		{
		CopyStringToBuffer(fileName, HelpFile, LABELSIZE);
		}
	else
		{
		if (!getString(ns, fileName, 9, TRUE, ECHO, ns))
			{
			return;
			}
		}

	normalizeString(fileName);

	if (!*fileName)
		{
		dispHlp(H_DOHELP);
		return;
		}

	if (fileName[0] == '?')
		{
		ShowHelpMenu();
		}
	else if (SameString(fileName, getmsg(1593)))
		{
		CRmPrintfCR(getmsg(1594));
		}
	else
		{
		strupr(fileName);

		Bool ShownInternal = FALSE;
		Bool WhichOnes[MAXHLP];

		for (HelpFiles theFile = (HelpFiles) 0; theFile < MAXHLP;
				theFile = (HelpFiles) (theFile + 1))
			{
			if (u_match(hlpnames[theFile], fileName))
				{
				dispHlp(theFile);
				WhichOnes[theFile] = ShownInternal = TRUE;

				if (TI()UserControl.GetOutFlag() == OUTSKIP)
					{
					return;
					}
				}
			else
				{
				WhichOnes[theFile] = FALSE;
				}
			}

		// adding the extention makes things look simpler for the user...
		// and restricts the files which can be read
		strcat(fileName, getmsg(1436));

		changedir(cfg.helppath);
		directoryinfo *files = filldirectory(fileName, 1, OldAndNew, FALSE);

		if ((files && files[0].name[0]) || !ShownInternal)
			{
			delete [] files;
			files = NULL;

			tutorial(fileName, WhichOnes);
			}

		delete [] files;
		}
	}


// --------------------------------------------------------------------------
// doIntro(): handles Intro to .... command.

void doIntro(void)
	{
	SetDoWhat(READINTRO);

	mPrintfCR(getmsg(264), cfg.nodeTitle);

	dispHlp(H_INTRO);
	}


// --------------------------------------------------------------------------
// doLogout(): Handles T(erminate) command.

void doLogout(Bool expand, char first)
	{
	char done = FALSE;

	SetDoWhat(DOLOGOUT);

	TI()MRO.Verbose = FALSE;

	if (expand)
		{
		first = 0;
		}

	mPrintf(getmsg(285));

	if (first == 'q')
		{
		TI()MRO.Verbose = TRUE;
		}

	int OptionCounter = 0;

	while (!done && (HaveConnectionToUser()))
		{
		done = TRUE;

		if (!first)
			{
			if (++OptionCounter == 69)
				{
				mPrintf(getmsg(1294), getmsg(69), spc);
				OptionCounter = 0;
				}
			}

		int ich;
		switch (toupper(first ? (int) first : (ich = iCharNE())))
			{
			case 'Y':
			case 'Q':
				{
				SetDoWhat(TERMQUIT);

				mPrintfCR(getmsg(287));

				if (!expand)
					{
					if (!getYesNo(getmsg(57), 0))
						{
						break;
						}
					}

				if (!(HaveConnectionToUser()))
					{
					break;
					}

				terminate(TRUE);

				// do not exit when doing .TS
				if (slv_door)
					{
					TI()ExitToMsdos = TRUE;
					}

				break;
				}

			case 'S':
				{
				SetDoWhat(TERMSTAY);

				mPrintfCR(getmsg(288));
				terminate(!TI()modStat && !slv_door);
				break;
				}

			case 'V':
				{
				mPrintf(getmsg(279));
				TI()MRO.Verbose = 2;
				done = FALSE;
				break;
				}

			case ESC:
				{
				break;
				}

			default:
				{
				oChar(ich);
				mPrintf(sqst);

				if (TI()CurrentUser->IsExpert())
					{
					break;
					}
				}

			case '?':
				{
				if (ich == '?')
					{
					oChar('?');
					}

				SetDoWhat(TERMMENU);

				showMenu(M_TERMINATE);
				break;
				}
			}

		first = 0;
		}
	}


// --------------------------------------------------------------------------
// doXpert()

void doXpert(void)
	{
	mPrintfCR(getmsg(315), TI()CurrentUser->IsExpert() ?
			getmsg(316) : getmsg(317));
	TI()CurrentUser->SetExpert(!TI()CurrentUser->IsExpert());
	}


// --------------------------------------------------------------------------
// doVerbose()

void doverbose(void)
	{
	mPrintfCR(getmsg(318), TI()CurrentUser->IsAutoVerbose() ?
			getmsg(316) : getmsg(317));
	TI()CurrentUser->SetAutoVerbose(!TI()CurrentUser->IsAutoVerbose());
	}


// --------------------------------------------------------------------------
// doSmallChat(): Does it.

static Bool CheckDatForMore(const char *String)
	{
	discardable *d;
	Bool toReturn = FALSE;

	if ((d = readData(17, 0, 0)) != NULL)
		{
		const char **Data = (const char **) d->aux;

		for (int Index = 0; Data[Index][0] != '#'; Index++)
			{
			if (SameString(Data[Index], String))
				{
				discardData(d);

				if ((d = readData(17, Index + 1, Index + 1)) != NULL)
					{
					Data = (const char **) d->aux;

					if (Data[0][0] == '*')
						{
						int RandomIndex = (int) random(atoi(Data[0] + 1));

						for (int EntryIndex = 1; RandomIndex;
								RandomIndex--, EntryIndex++)
							{
							while (Data[EntryIndex + 1][0] != '!')
								{
								EntryIndex++;
								}
							}

						doCR();
						do
							{
							mFormat(Data[EntryIndex] + 
									(Data[EntryIndex][0] == '!' ? 1 : 0));
							} while (Data[++EntryIndex][0] != '!');

						doCR();
						}
					else
						{
						Bool Repeat = FALSE;

						do
							{
							doCR();

							for (Index = 0; !SameString(Data[Index], getmsg(1561));
									Index++)
								{
								if (Data[Index][0] == '#')
									{
									Repeat = TRUE;
									}
								else
									{
									mFormat(Data[Index]);
									mFormat(bn);
									}
								}
							} while (Repeat &&
									TI()UserControl.GetOutFlag() == OUTOK);

						doCR();
						}
					discardData(d);
					toReturn = TRUE;
					}
				return (toReturn);
				}
			}

		discardData(d);
		}
	return (toReturn);
	}

void doSmallChat(void)
	{
	Bool first = TRUE;
	SetDoWhat(DOSMALLCHAT);

	oChar(';');

	getString(ns, TI()ContextSensitiveScriptText, 255, FALSE, ECHO, ns);

	if (*TI()ContextSensitiveScriptText == ';')
		{
		const char *ChatText1 = TI()ContextSensitiveScriptText + 1;// skip ';'

		if (SameString(ChatText1, getmsg(1562)))
			{
			mPrintf(getmsg(1563), TI()CurrentUser->IsProblem() ? '.' : '!');
			}
		else if (SameString(ChatText1, getmsg(1564)))
			{
			label LobbyName;

			// see note in stepRoom() for why we do this strcpy().

			strcpy(LobbyName, GetRoomName(LOBBY));

			gotoRoom(LobbyName, FALSE, FALSE, FALSE);
			}
		else if (SameString(ChatText1, getmsg(1697)))
			{
			TI()CurrentUser->SetRoman(!TI()CurrentUser->IsRoman());

			CRmPrintfCR(TI()CurrentUser->IsRoman() ? getmsg(317) : getmsg(316));
			}
		else if (SameString(ChatText1, getmsg(1565)))
			{
			int i = 0;

			CRmPrintf(getmsg(1566));
			while (!BBSCharReady() && HaveConnectionToUser())
				{
				static const char twirlstring[] = {'/', '-', '\\', '|'};

				if (!first) twirlypause(15);
				first = FALSE;
				TI()SerialPort.Output('\b');
				outConRawBs();
				TI()SerialPort.Output('\b');
				outConRawBs();
				TI()SerialPort.Output(twirlstring[i]);
				outCon(twirlstring[i]);
				TI()SerialPort.Output(']');
				outCon(']');
				i++;
				if (i == 4) i = 0;

				if (CheckIdleUserTimeout(TRUE))
					{
					TI()sleepkey = FALSE;
					time(&TI()LastActiveTime);

					UserHasTimedOut();
					}
				else
					{
					TI()sleepkey = FALSE;
					}
				}

			doCR();

			if (BBSCharReady())
				{
				iCharNE();
				}
			}
#if (VERSION != RELEASE) 
		else if (SameString(ChatText1, "fmt"))
			{
			label l, m;
			getString("format string", l, LABELSIZE, FALSE, ECHO, ns);
			const Bool premature = getYesNo("Premature", 0);
			const Bool inclPrompt = getYesNo("Include prompt", 0);
			doCR();
			getFmtString(l, m, premature, inclPrompt);
			mPrintfCR(pcts, m);
			}
		else if (SameString(ChatText1, "debug"))
			{
			if (debug)
				{
				TurnDebugOff();
				CRmPrintfCR(getmsg(1626), getmsg(316));
				}
			else
				{
				if (TurnDebugOn())
					{
					CRmPrintfCR(getmsg(1626), getmsg(317));
					}
				else
					{
					CRmPrintfCR(getmsg(19));
					}
				}
			}
		else if (SameString(ChatText1, "compact memory"))
			{
			const int Value = (int) getNumber("level", 0, 1, 0, FALSE, NULL);
			compactMemory(Value);
			doCR();
			}
		else if (SameString(ChatText1, "read messages"))
			{
			label l;
			getString("messages to read", l, LABELSIZE, FALSE, ECHO, ns);

			Bool res = FALSE;
			if (SameString(l, "net"))
				{
				res = read_net_messages();
				}
			else if (SameString(l, "sys"))
				{
				res = read_sys_messages();
				}
			else if (SameString(l, "cfg"))
				{
				res = read_cfg_messages();
				}
			else if (SameString(l, "ec"))
				{
				res = read_ec_messages();
				}
			else if (SameString(l, "fs"))
				{
				res = read_fs_messages();
				}
			else if (SameString(l, "cw"))
				{
				res = read_cw_messages();
				}
			else if (SameString(l, "zm"))
				{
				res = read_zm_messages();
				}

			if (res)
				{
				mPrintfCR("%s messages read.", l);
				}
			else
				{
				mPrintfCR("Could not read %s messages", l);
				}
			}
		else if (SameString(ChatText1, "dump messages"))
			{
			label l;
			getString("messages to dump", l, LABELSIZE, FALSE, ECHO, ns);

			Bool res = FALSE;
			if (SameString(l, "net"))
				{
				res = TRUE;
				dump_net_messages();
				}
			else if (SameString(l, "sys"))
				{
				res = TRUE;
				dump_sys_messages();
				}
			else if (SameString(l, "cfg"))
				{
				res = TRUE;
				dump_cfg_messages();
				}
			else if (SameString(l, "ec"))
				{
				res = TRUE;
				dump_ec_messages();
				}
			else if (SameString(l, "fs"))
				{
				res = TRUE;
				dump_fs_messages();
				}
			else if (SameString(l, "cw"))
				{
				res = TRUE;
				dump_cw_messages();
				}
			else if (SameString(l, "zm"))
				{
				res = TRUE;
				dump_zm_messages();
				}

			if (res)
				{
				mPrintfCR("%s messages dumped.", l);
				}
			else
				{
				mPrintfCR("What?");
				}
			}
	#if !defined(WINCIT)
		else if (SameString(ChatText1, "saver on"))
			{
			if (ScreenSaver.TurnOn())
				{
				pause(50);
				while (kb_hit());

				CRmPrintfCR("Screen saver is now on.");
				}
			else
				{
				CRmPrintfCR(getmsg(1637));
				}
			}
		else if (SameString(ChatText1, "saver off"))
			{
			ScreenSaver.TurnOff();

			CRmPrintfCR("Screen saver is now off.");
			}
		else if (SameString(ChatText1, "memory dump"))
			{
			label l;

			getString("segment", l, LABELSIZE, FALSE, ECHO, ns);
			const uint seg = (uint) strtoul(l, NULL, 16);

			getString("offset", l, LABELSIZE, FALSE, ECHO, "0004");
			uint off = (uint) strtoul(l, NULL, 16);

			long len = getNumber("length", 0, UINT_MAX, 20 * 16, FALSE, NULL);

			for (; len > 0; len -= 16)
				{
				uchar val[16];

				for (int x = 0; x < 16; x++)
					{
					val[x] = *((uchar *) MK_FP(seg, off++));
					}

// For stripping out control characters...
#define nc(a) (a < 32 ? 32 : a)

				CRmPrintf("%p  %02x %02x %02x %02x %02x %02x %02x %02x "
						" %02x %02x %02x %02x %02x %02x %02x %02x  "
						"%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",
						MK_FP(seg, off - 16),
						val[0], val[1], val[2], val[3],
						val[4], val[5], val[6], val[7],
						val[8], val[9], val[10], val[11],
						val[12], val[13], val[14], val[15],
						nc(val[0]), nc(val[1]), nc(val[2]), nc(val[3]),
						nc(val[4]), nc(val[5]), nc(val[6]), nc(val[7]),
						nc(val[8]), nc(val[9]), nc(val[10]), nc(val[11]),
						nc(val[12]), nc(val[13]), nc(val[14]), nc(val[15])
						);
				}

			doCR();
			}
		else if (SameString(ChatText1, "memory"))
			{
			static void *ptr;

			if (ptr)
				{
				farfree(ptr);
				ptr = NULL;
				}

			const long memorysize = getNumber("amount (bytes)", 0, LONG_MAX,
					0, FALSE, NULL);

			if (memorysize)
				{
//				ptr = farmalloc(memorysize);
				}

			CRmPrintfCR("Memory allocated");
//			, ptr ? ns : "not ");

			char foo[128];
			sprintf(foo, "Allocated %ld memory", memorysize);
			trap(foo, T_HACK);
			amPrintf("User %s allocated %ld memory\n",
					TI()CurrentUser->GetName(), memorysize);
			SaveAideMess(NULL);

			}
	#endif
#endif
		else if (!CheckDatForMore(ChatText1))
			{
			doEvent(EVT_SMALLCHAT); // User configurable is least important
			}
		}
	}

void doY(Bool backwards)
	{
	discardable *d = readData(6, 2, 2);

	if (d)
		{
		int HighestIndex = atoi(((const char **) d->aux)[0]) - 1;

		if (backwards)
			{
			TI()Ycounter--;
			}
		else
			{
			TI()Ycounter++;
			}

		if (TI()Ycounter < 0)
			{
			TI()Ycounter = HighestIndex;
			}
		else if (TI()Ycounter > HighestIndex)
			{
			TI()Ycounter = 0;
			}

		mPrintf(((const char **) d->aux)[TI()Ycounter + 1]);

		discardData(d);
		}
	else
		{
		mPrintf(getmsg(188), getmsg(1428));
		}
	}

Bool doPoop(char c)
	{
	if (toupper(c) == 'P' || cfg.poop < 0)
		{
		if (c == 'P')
			{
			TI()CurrentUser->SetPoopcount(TI()CurrentUser->GetPoopcount() +
					cfg.poop < 0 ? 1 : 69);
			}
		else if (c == 'p')
			{
			TI()CurrentUser->SetPoopcount(TI()CurrentUser->GetPoopcount() + 1);
			}
		else if (c == 'S')
			{
			TI()CurrentUser->SetPoopcount(TI()CurrentUser->GetPoopcount() + 69000l);
			}
		else // s
			{
			TI()CurrentUser->SetPoopcount(TI()CurrentUser->GetPoopcount() + 100);
			}

		if ((TI()CurrentUser->GetPoopcount() > cfg.maxpoop) && TI()loggedIn)
			{
			cfg.maxpoop = TI()CurrentUser->GetPoopcount();
			CopyStringToBuffer(cfg.poopuser, TI()CurrentUser->GetName(),
					LABELSIZE);
			}

		if ((TI()CurrentUser->IsExpert() && (cfg.poop == 1)) || cfg.poop < 1)
			{
			if (TI()CurrentUser->GetPoopcount() < 0)
				{
				mPrintf(toupper(c) == 'P' ? getmsg(1417) : getmsg(1418));
				}
			else
				{
				mPrintf(toupper(c) == 'P' ? getmsg(1419) : getmsg(1420));
				}
			}

		return (FALSE);
		}
	else
		{
		return (TRUE);
		}
	}

void doWow(Bool dot)
	{
	if (dot)
		{
		mPrintfCR(getmsg(1568));
		TI()wowcount = GetNumberWithBlurb(getmsg(1567), LONG_MIN, LONG_MAX,
				TI()wowcount, B_WOWCOUNT);
		}
	else
		{
		TI()wowcount++;

		if (TI()wowcount < -2)
			{
			mPrintf(getmsg(1569), ltoac(-1 * TI()wowcount));

			if (TI()wowcount == -69)
				{
				mPrintf(getmsg(1294), spc, getmsg(69));
				}
			}
		else if (TI()wowcount < 0)
			{
			if (TI()wowcount == -2)
				{
				mPrintf(getmsg(1570));
				}

			mPrintf(getmsg(1571));
			}
		else if (TI()wowcount == 0)
			{
			mPrintf(getmsg(1572));
			}
		else if (TI()wowcount > 2)
			{
			mPrintf(getmsg(1573), ltoac(TI()wowcount));

			if (TI()wowcount == 69)
				{
				mPrintf(getmsg(1294), spc, getmsg(69));
				}
			}
		else
			{
			if (TI()wowcount == 2)
				{
				mPrintf(getmsg(1570));
				}

			mPrintf(getmsg(1574));
			}
		}
	}


// --------------------------------------------------------------------------
// doBorder(): print a border line.

void doBorder(void)
	{
	assert(borders != NULL);

	if (!TI()CurrentUser->IsViewBorders())
		{
		return;
		}

	if (++TI()borderCounter == cfg.borderfreq)
		{
		int tries;
		TI()borderCounter = 0;

		for (TI()lastBorder = (TI()lastBorder + 1) % cfg.maxborders, tries = 0;
				tries <= cfg.maxborders;
				TI()lastBorder = (TI()lastBorder + 1) % cfg.maxborders, tries++)
			{
			if (borders[TI()lastBorder * (BORDERSIZE + 1)])
				{
				CRmPrintf(pcts, &(borders[TI()lastBorder * (BORDERSIZE + 1)]));
				break;
				}
			}
		}
	}

void doFingerEdit(void)
	{
	SetDoWhat(FINGEREDIT);

	char *EditBuffer = new char[MAXTEXT];

	if (EditBuffer)
		{
		if (TI()CurrentUser->GetFinger())
			{
			CopyStringToBuffer(EditBuffer,
					TI()CurrentUser->GetFinger(), MAXTEXT - 1);
			}
		else
			{
			EditBuffer[0] = 0;
			}

		mPrintfCR(getmsg(201));
		doCR();

		if (getText(NULL, EditBuffer))
			{
			TI()CurrentUser->SetFinger(EditBuffer);

			if (TI()loggedIn)
				{
				TI()CurrentUser->LogExtensions::Save(TI()ThisLog,
					TI()CurrentUser->GetName());
				}
			}

		delete [] EditBuffer;
		}
	else
		{
		mPrintf(getmsg(188), getmsg(199));
		}
	}

void doFingerList(void)
	{
	SetDoWhat(FINGERLIST);

	TI()UserControl.SetOutFlag(OUTOK);

	CRmPrintfCR(getmsg(63), cfg.Uusers_nym);

	prtList(LIST_START);

	for (l_slot logslot = 0; logslot < cfg.MAXLOGTAB &&
			!TI()UserControl.CheckInput(FALSE); logslot++)
		{
		if (LogTab->GetEntry(logslot)->IsInuse())
			{
			LogExtensions LE(0);

			LE.Load(LogTab->GetEntry(logslot)->GetLogIndex());

			if (LE.GetFinger())
				{
				prtList(LogTab->GetEntry(logslot)->GetName());
				}
			}
		}

	prtList(LIST_END);
	}

void doFingerUser(void)
	{
	SetDoWhat(FINGERVIEW);

	label Name;
	do
		{
		getString(cfg.Luser_nym, Name, LABELSIZE, TRUE, ECHO, ns);

		normalizeString(Name);

		if (*Name == '?')
			{
			ListUsers(TI()CurrentUser->IsAide());
			}
		} while (*Name == '?');

	if (*Name)
		{
		const l_slot logslot = FindPersonByPartialName(Name);

		if (logslot == CERROR)
			{
			CRmPrintfCR(getmsg(595), Name);
			}
		else
			{
			LogExtensions LE(0);

			LE.Load(LogTab->GetEntry(logslot)->GetLogIndex());

			doCR();

			if (LE.GetFinger())
				{
				mFormat(LE.GetFinger());
				}
			else
				{
				mPrintf(getmsg(373),
						LogTab->GetEntry(logslot)->GetName());
				}

			doCR();
			}
		}
	}

void doFinger(void)
	{
	SetDoWhat(FINGER);

	mPrintf(getmsg(196));

	int ich;
	switch (toupper(ich = iCharNE()))
		{
		case 'D':
			{
			mPrintfCR(getmsg(354));
			doCR();

			if (TI()CurrentUser->GetFinger())
				{
				if (getYesNo(getmsg(355), 0))
					{
					TI()CurrentUser->SetFinger(ns);
					}
				}

			break;
			}

		case 'E':
			{
			mPrintfCR(getmsg(197));
			doCR();

			doFingerEdit();

			break;
			}

		case 'L':
			{
			mPrintfCR(getmsg(340));

			doFingerList();

			break;
			}

		case 'U':
			{
			mPrintfCR(pcts, cfg.Uuser_nym);

			doFingerUser();

			break;
			}

		case ESC:
			{
			break;
			}

		default:
			{
			oChar(ich);
			mPrintf(sqst);

			if (TI()CurrentUser->IsExpert())
				{
				break;
				}
			}

		case '?':
			{
			if (ich == '?')
				{
				oChar('?');
				}

			SetDoWhat(FINGERMENU);

			showMenu(M_FINGER);
			break;
			}
		}
	}

void doReadBull(Bool FromRoomPrompt)
	{
	if (FromRoomPrompt)
		{
		mPrintfCR(getmsg(1690));
		}

	long bmenuOff;
	label menuFile;

	if ((bmenuOff = getMnuOff(M_BULLETINS, menuFile)) > 0)
		{
		doCR();

		changedir(cfg.helppath);

		FILE *fl;

		if ((fl = fopen(menuFile, FO_R)) != NULL)
			{
			int ich = '?';
			Bool found;

			do
				{
				found = ich == '?';
				TI()UserControl.SetOutFlag(OUTOK);

				fseek(fl, bmenuOff, SEEK_SET);

				if (ich == '?')
					{
					char line[MAXWORD];

					while (fgets(line, MAXWORD, fl) &&
							(TI()UserControl.GetOutFlag() != OUTNEXT) &&
							(TI()UserControl.GetOutFlag() != OUTSKIP) &&
							!TI()UserControl.CheckInput(FALSE) &&
							line[0] != '#')
						{
						if (line[0] != '!')
							{
							mFormat(line);
							}
						else
							{
							char *words[256];

							parse_it(words, line);
							if (atoi(words[2]))
								{
								CRmPrintf(getmsg(1579), toupper(words[1][0]),
										words[3]);
								}
							}
						}

					ResetOutputControl();
					}
				else
					{
					char line[MAXWORD];

					while (fgets(line, MAXWORD, fl) && line[0] != '#')
						{
						if (line[0] == '!')
							{
							char *words[256];

							parse_it(words, line);
							if (toupper(words[1][0]) == ich)
								{
								if (!TI()CurrentUser->IsExpert())
									{
									CRmPrintfCR(getmsg(1005));
									}

								dumpf(words[0] + 1, TRUE, FALSE);
								ResetOutputControl();
								found = TRUE;
								}
							}
						}
					}

				doCR();

				if (!found)
					{
					CRmPrintfCR(getmsg(765));
					}

				TI()numLines = 0;
				CRmPrintf(getmsg(764));

				ich = toupper(iCharNE());
				if (ich == '?')
					{
					doCR();
					}
				} while (ich != '\r' && ich != '\n' &&
						HaveConnectionToUser());

			fclose(fl);
			}
		}
	else
		{
		dispBlb(B_BULLETIN);
		doCR();
		}

	changedir(cfg.homepath);
	}
