// --------------------------------------------------------------------------
// Citadel: Misc3.CPP
//
// Overlayed miscellaneous stuff that is not used often; compare with
// Misc2.CPP.

#include "ctdl.h"
#pragma hdrstop

#include "log.h"
#include "config.h"
#include "msg.h"
#include "fscfg.h"
#include "scrlback.h"
#include "timer.h"
#include "readcfg.h"
#include "menus.h"
#include "blurbs.h"
#include "helpfile.h"
#include "aplic.h"
#include "cfgfiles.h"
#include "cwindows.h"
#include "timechk.h"
#include "miscovl.h"


// --------------------------------------------------------------------------
// Contents
//
// offhook()		sysop fn: to take modem off hook
// ringSystemREQ()	signals a system request for 2 minutes.
// dial_out()		dial out to other boards
// greeting()		System-entry blurb etc
// cCPrintf()		send formatted output to console, centered
// logo()			prints out system logo at startup
// editBorder() 	edit a border line.
// parseArgs()		sets global flags baised on command line


extern ConsoleLockC ConsoleLock;
extern int FilesRequired;

void GetSysPWHandler(const char *PW);


// --------------------------------------------------------------------------
// ringSystemREQ(): signals a system request for 2 minutes.

void ringSystemREQ(void)
	{
	int row, col;
	Bool answered = FALSE;

	TI()altF3Timeout = 0;

	doccr();
	doccr();
	readpos(&row, &col);
	(*stringattr)(row, getmsg(506), (uchar)(cfg.wattr | 128), FALSE);
	StatusLine.Update();
	doccr();

	ScreenSaver.Update();

	Hangup();

	answered = FALSE;
	for (int i = 0; (i < 120) && !answered; i++)
		{
		outCon(BELL);
		pause(100);

		if (KBReady())
			{
			answered = TRUE;
			}
		}

	if (!answered)
		{
		CITWINDOW *w = ScreenSaver.IsOn() ? NULL : 
				CitWindowsMsg(NULL, getmsg(1611));

		Initport();

		if (w)
			{
			destroyCitWindow(w, FALSE);
			}
		}

	StatusLine.Update();
	}


// --------------------------------------------------------------------------
// dial_out(): dial out to other boards

void dial_out(void)
	{
	TimeChangeCheckerC UpdateTime;
	Bool LocalEcho = FALSE;
	Bool quit = FALSE;

	SetDoWhat(DIALOUT);

	TI()UserControl.SetOutFlag(IMPERVIOUS);

	if (!rlmEvent(LT_STARTDIALOUT, (long) &LocalEcho, (long) &quit))
		{
		doCR();
		dispBlb(B_DIALOUT);
		}

	CITWINDOW *w = CitWindowsMsg(NULL, getmsg(1611));

	Hangup();

	TI()SerialPort.Enable();

	TI()SerialPort.SetSpeed(cfg.initbaud);
	StatusLine.Update();

	outstring(cfg.dialsetup);
	outstring(br);

	pause(100);

	if (w)
		{
		destroyCitWindow(w, FALSE);
		w = NULL;
		}

	TI()callout = TRUE;

	do
		{
		// If some script kicked in and changed outflag...
		if (!TI()UserControl.CanOutput())
			{
			TI()UserControl.SetOutFlag(IMPERVIOUS);
			}

		dialout_fkey = 0;

		if (KBReady())
			{
			char con = (char) ciChar();

			if (con)
				{
				TI()SerialPort.Output(con);

				if (LocalEcho)
					{
					oChar(con);
					}
				}
			}

		if (TI()SerialPort.IsInputReady())
			{
			char mod = (char) TI()SerialPort.Input();

			if (mod == '\n')
				{
				doccr();
				}
			else if (mod != '\r')
				{
				oChar(mod);
				}
			}

		// handle alt-keys
		if (dialout_fkey)
			{
			if (!rlmEvent(LT_DIALOUTKEY, dialout_fkey, 0))
				{
				switch (dialout_fkey)
					{
					case ALT_D:
						{
						if (!ConsoleLock.IsLocked())
							{
							if (debug)
								{
								TurnDebugOff();

								if (cfg.VerboseConsole)
									{
									CitWindowsNote(NULL, getmsg(1626), getmsg(316));
									}
								}
							else
								{
								if (TurnDebugOn())
									{
									if (cfg.VerboseConsole)
										{
										CitWindowsNote(NULL, getmsg(1626), getmsg(317));
										}
									}
								else
									{
									if (cfg.VerboseConsole)
										{
										CitWindowsNote(NULL, getmsg(19));
										}
									}
								}
							}
						else
							{
							if (cfg.VerboseConsole)
								{
								CitWindowsNote(NULL, getmsg(1635));
								}
							}

						break;
						}

					case ALT_E:
						{
						if (!ConsoleLock.IsLocked())
							{
							shellescape(FALSE);
							}
						else
							{
							if (cfg.VerboseConsole)
								{
								CitWindowsNote(NULL, getmsg(1635));
								}
							}

						break;
						}

					case ALT_H:
						{
						CITWINDOW *w = ScreenSaver.IsOn() ? NULL :
								CitWindowsMsg(NULL, getmsg(1643));

						Hangup();
						TI()SerialPort.Enable();	// ?? RAF ??

						if (w)
							{
							destroyCitWindow(w, FALSE);
							}

						break;
						}

					case ALT_K:
						{
						DisplayScrollBackBuffer();
						break;
						}

					case ALT_L:
						{
						if (ConsoleLock.IsLocked())
							{
							if (ConsoleLock.MayUnlock())
								{
								GetStringFromConsole(getmsg(781), LABELSIZE,
										ns, GetSysPWHandler, TRUE);
								}
							else
								{
								if (cfg.VerboseConsole)
									{
									CitWindowsNote(NULL, getmsg(1636));
									}
								}
							}
						else if (*cfg.f6pass)
							{
							ConsoleLock.Lock();
							if (cfg.VerboseConsole)
								{
								CitWindowsNote(NULL, getmsg(1628));
								}
							}
						else
							{
							if (cfg.VerboseConsole)
								{
								CitWindowsNote(NULL, getmsg(1629),
									citfiles[C_CONFIG_CIT]);
								}
							}

						break;
						}

					case ALT_P:
						{
						LocalEcho = !LocalEcho;

						if (cfg.VerboseConsole)
							{
							CitWindowsNote(NULL, getmsg(711), LocalEcho ?
									getmsg(317) : getmsg(316));
							}

						break;
						}

					case ALT_Q:
						{
						doccr();
						cPrintf(getmsg(712));
						quit = TRUE;
						break;
						}

					case ALT_S:
						{
						if (!ConsoleLock.IsLocked())
							{
							shellescape(TRUE);
							}
						else
							{
							if (cfg.VerboseConsole)
								{
								CitWindowsNote(NULL, getmsg(1635));
								}
							}

						break;
						}

					case ALT_V:
						{
						if (!ConsoleLock.IsLocked())
							{
							if (StatusLine.ToggleFullScreen())
								{
								StatusLine.Update();
								}
							else
								{
								if (cfg.VerboseConsole)
									{
									CitWindowsNote(NULL, getmsg(1613));
									}
								}
							}
						else
							{
							if (cfg.VerboseConsole)
								{
								CitWindowsNote(NULL, getmsg(1635));
								}
							}

						break;
						}

					case PGUP:
						{
						PortSpeedE NewSpeed = (PortSpeedE)
								((TI()SerialPort.GetSpeed() + 1) % PS_NUM);

						CITWINDOW *w = (ScreenSaver.IsOn() || !cfg.baudPause) ?
								NULL : CitWindowsMsg(NULL, getmsg(1641),
								bauds[NewSpeed]);

						TI()SerialPort.SetSpeed(NewSpeed);

						if (w)
							{
							destroyCitWindow(w, FALSE);
							}

						StatusLine.Update();
						break;
						}

					case PGDN:
						{
						PortSpeedE NewSpeed = (PortSpeedE)
								(TI()SerialPort.GetSpeed() - 1);
						if (NewSpeed < PS_300)
							{
							NewSpeed = (PortSpeedE) (PS_NUM - 1);
							}


						CITWINDOW *w = (ScreenSaver.IsOn() || !cfg.baudPause) ?
								NULL : CitWindowsMsg(NULL, getmsg(1641),
								bauds[NewSpeed]);

						TI()SerialPort.SetSpeed(NewSpeed);

						if (w)
							{
							destroyCitWindow(w, FALSE);
							}

						StatusLine.Update();
						break;
						}

					default:
						{
						int dm_ofs = -1;

						// Tell me: is this ugly?
						if (dialout_fkey >= F1 && dialout_fkey <= F10)
							{
							dm_ofs = dialout_fkey - F1 + DM_F1;
							}
						else if (dialout_fkey == F11 || dialout_fkey == F12)
							{
							dm_ofs = dialout_fkey - F11 + DM_F11;
							}
						else if (dialout_fkey >= ALT_F1 && dialout_fkey <= ALT_F10)
							{
							dm_ofs = dialout_fkey - ALT_F1 + DM_ALT_F1;
							}
						else if (dialout_fkey >= ALT_F11 || dialout_fkey == ALT_F12)
							{
							dm_ofs = dialout_fkey - ALT_F11 + DM_ALT_F11;
							}
						else if (dialout_fkey >= SFT_F1 && dialout_fkey <= SFT_F10)
							{
							dm_ofs = dialout_fkey - SFT_F1 + DM_SFT_F1;
							}
						else if (dialout_fkey >= SFT_F11 || dialout_fkey == SFT_F12)
							{
							dm_ofs = dialout_fkey - SFT_F11 + DM_SFT_F11;
							}
						else if (dialout_fkey >= CTL_F1 && dialout_fkey <= CTL_F10)
							{
							dm_ofs = dialout_fkey - CTL_F1 + DM_CTL_F1;
							}
						else if (dialout_fkey >= CTL_F11 || dialout_fkey == CTL_F12)
							{
							dm_ofs = dialout_fkey - CTL_F11 + DM_CTL_F11;
							}

						if (dm_ofs > -1)
							{
							outstring(cfg.dialmacro[dm_ofs]);

							if (LocalEcho)
								{
								cPrintf(cfg.dialmacro[dm_ofs]);
								}
							}
						}
					}
				}
			}

		// Update time on status line
		if (UpdateTime.Check())
			{
			if (TI()CronMonitor)
				{
				(TI()CronMonitor->func)(EVT_DRAWINT, 0, 0, TI()CronMonitor);
				}

			StatusLine.Update();
			UpdateTime.Reset();
			}
		} while (!quit);

	TI()callout = FALSE;

	w = CitWindowsMsg(NULL, getmsg(1611));

	Initport();

	if (cfg.offhook && TI()loggedIn)
		{
		offhook();
		}

	if (w)
		{
		destroyCitWindow(w, FALSE);
		w = NULL;
		}

	rlmEvent(LT_ENDDIALOUT, 0, 0);

	SetDoWhat(DUNO);

	Cron.ResetTimer();
	time(&TI()LastActiveTime);

	doCR();
	}


// --------------------------------------------------------------------------
// cCPrintf(): send formatted output to console, centered
//
// Input:
//	const char *fmt: The format string, as in printf()
//	...: What to format, as in printf()

void cdecl cCPrintf(const char *fmt, ...)
	{
	va_list ap;
	int i;
	int row, col;

	va_start(ap, fmt);
	vsprintf(TI()OC.prtf_buff, fmt, ap);
	va_end(ap);

	i = (conCols - strlen(TI()OC.prtf_buff)) / 2;

	strrev(TI()OC.prtf_buff);

	while (i--)
		{
		strcat(TI()OC.prtf_buff, spc);
		}

	strrev(TI()OC.prtf_buff);

	readpos(&row, &col);
	(*stringattr)(row, TI()OC.prtf_buff, cfg.attr, TRUE);
	}


// --------------------------------------------------------------------------
// logo(): Prints out system logo at startup.

#ifdef WINCIT
long FAR PASCAL logoWndProc(HWND hwnd, WORD message, WORD wParam, LONG lParam)
	{
	switch (message)
		{
		case WM_PAINT:
			{
			PAINTSTRUCT ps;
			HDC hdc = BeginPaint(hwnd, &ps);
			HBITMAP hBMP = LoadBitmap(hInstance, "CITADELLOGO");

			if (hBMP)
				{
				DrawBitmap(hdc, hBMP, 0, 0);
				DeleteObject(hBMP);
				}

			EndPaint(hwnd, &ps);
			return (0);
			}
		}
	return (DefWindowProc(hwnd, message, wParam, lParam));
	}

void logo(int turnOn)
	{
	static HWND logoWindow;

	if (turnOn)
		{
		if (!logoWindow)
			{
			logoWindow = CreateWindow("Citadel Logo", programName,
					WS_POPUP, GetSystemMetrics(SM_CXSCREEN) / 2 - 100,
					GetSystemMetrics(SM_CYSCREEN) / 2 - 20, 200, 40, NULL,
					NULL, hInstance, NULL);

			ShowWindow(logoWindow, SW_SHOW);
			UpdateWindow(logoWindow);
			}
		}
	else
		{
		if (logoWindow)
			{
			if (DestroyWindow(logoWindow))
				{
				logoWindow = NULL;
				}
			}
		}
	}
#else
void logo(int turnOn)
	{
	outPhys(TRUE);

	if (turnOn)
		{
		int i;

		cls(SCROLL_NOSAVE);

		getScreenSize((&conCols), (&conRows));

		for (i = 0; i < ((conRows/2) -1); i++)
			{
			doccr();
			}

		cCPrintf(getmsg(750), cfg.softverb, cfg.softverb[0] ? spc : ns,
				programName, version);
		doccr();

		cCPrintf(pcts, Author);
		doccr();
		doccr();
		}
	else
		{
		cls(SCROLL_SAVE);
		}

	outPhys(FALSE);
	}
#endif

static void generateHelpFiles(void)
	{
	VerifyHeap();

	printf(getmsg(16));

	for (int i = 0; i < MAXHLP; i++)
		{
		VerifyHeap();

		discardable *d;

		if ((d = readData(14, i, i)) != NULL)
			{
			VerifyHeap();

			if (i)
				{
				printf(getmsg(1509));
				}

			label fn;
			sprintf(fn, getmsg(1525), hlpnames[i]);
			printf(fn);

			FILE *fd;
			if ((fd = fopen(fn, FO_W)) != NULL)
				{
				for (int j = 0; ((const char **) d->aux)[j][0] != '#'; j++)
					{
					fprintf(fd, getmsg(1294), ((const char **) d->aux)[j], bn);
					}

				fclose(fd);
				}
			else
				{
				printf(getmsg(12));
				}

			discardData(d);
			}
		else
			{
			printf(getmsg(13));
			}
		}

	for (i = 0; i < MAXBLBS; i++)
		{
		VerifyHeap();

		discardable *d;

		if ((d = readData(13, i, i)) != NULL)
			{
			VerifyHeap();

			printf(getmsg(1509));

			label fn;
			sprintf(fn, getmsg(1526), blbnames[i]);
			printf(fn);

			FILE *fd;
			if ((fd = fopen(fn, FO_W)) != NULL)
				{
				for (int j = 0; ((const char **) d->aux)[j][0] != '#'; j++)
					{
					fprintf(fd, getmsg(1294), ((const char **) d->aux)[j], bn);
					}

				fclose(fd);
				}
			else
				{
				printf(getmsg(12));
				}

			discardData(d);
			}
		else
			{
			printf(getmsg(13));
			}
		}

	VerifyHeap();

	printf(getmsg(1294), getmsg(1509), ctdlMnu);
	discardable *d;

	if ((d = readData(11)) != NULL)
		{
		VerifyHeap();

		FILE *fd;
		if ((fd = fopen(ctdlMnu, FO_W)) != NULL)
			{
			VerifyHeap();

			discardable *c;
			int i;

			for (c = d, i = 0; i < MAXMENUS; i++,
					c = (discardable *) getNextLL(c))
				{
				VerifyHeap();

				// Don't write out optional menus.
				if (c && ((const char **) c->aux)[0][0] != '#')
					{
					fprintf(fd, getmsg(1524), menunames[i], bn);

					for (int j = 0; ((const char **) c->aux)[j][0] != '#';
							j++)
						{
						fprintf(fd, getmsg(1294), ((const char **) c->aux)[j],
								bn);
						}
					}
				}

			fclose(fd);
			}
		else
			{
			printf(getmsg(12));
			}

		discardData(d);
		}
	else
		{
		printf(getmsg(13));
		}

	printf(getmsg(11));

	VerifyHeap();
	}


// --------------------------------------------------------------------------
// parseArgs(): Sets global flags baised on command line.
//
// Input:
//	int argc: As in CRTL's main()
//	const char *argv[]: As in CRTL's main()

void parseArgs(int argc, const char *argv[])
	{
	int i;

	cfg.bios = TRUE;

	// logo gets white letters
	cfg.attr = 7;
	cfg.cattr = 15;
	cfg.uttr = 1;
	cfg.wattr = 0x70;

	for (i = 1; i < argc; i++)
		{
		if (argv[i][0] == '/' || argv[i][0] == '-')
			{
			switch (toupper((int) argv[i][1]))
				{
				case '0':
					{
					cmdLine[1] = 1;
					break;
					}

				case 'A':
				case 'L':
					{
					slv_door = TRUE;
					break;
					}

				case 'B':
					{
					const char *SpeedPtr;

					if (argv[i][2])
						{
						SpeedPtr = argv[i] + 2;
						}
					else
						{
						SpeedPtr = argv[++i];
						}

					slv_baud = digitbaud(atol(SpeedPtr));

					if (slv_baud == PS_ERROR)
						{
						printf(getmsg(3), SpeedPtr);

						DeinitializeTimer();
						critical(FALSE);
						exit(0);
						}

					for (ModemSpeedE j = (ModemSpeedE) 0; j < MS_NUM;
							j = (ModemSpeedE) (j + 1))
						{
						if (connectbauds[j] == bauds[slv_baud])
							{
							TI()ModemSpeed = j;
							break;
							}
						}

					slv_door = TRUE;
					break;
					}

				case 'C':
					{
					unlink(etcTab);

					unlink(lgTab);
					unlink(msgTab);
					unlink(rmTab);
					unlink(cronTab);

					reconfig = TRUE;
					break;
					}

				case 'D':
					{
					cfg.bios = 0;
					break;
					}

				case 'E':
					{
					readconfigcit = TRUE;
					break;
					}

				case 'F':
					{
					if (argv[i][2])
						{
						FilesRequired = atoi(argv[i] + 2);
						}
					else
						{
						i++;
						FilesRequired = atoi(argv[i]);
						}

					break;
					}

				case 'G':
					{
					if (argv[i][2])
						{
						changedir(argv[i] + 2);
						}

					generateHelpFiles();
					DeinitializeTimer();
					critical(FALSE);
					exit(0);
					break;	// useless, but it looks nifty.
					}

				case 'M':
					{
					conMode = atoi(argv[i]+2);
					break;
					}

				case 'N':
					{
					if (toupper(argv[i][2]) == 'B')
						{
						cmd_nobells = TRUE;
						}

					if (toupper(argv[i][2]) == 'C')
						{
						cmd_nochat = TRUE;
						}

					break;
					}

				case 'O':
					{
					if (!read_cfg_messages())
						{
						printf(getmsg(188), getmsg(1364));
						DeinitializeTimer();
						critical(FALSE);
						exit(0);
						}

					initExtDrivers();

					dump_cfg_messages();

					if (!FullScreenConfig())
						{
						DeinitializeTimer();
						critical(FALSE);
						exit(0);
						}

					break;
					}

				// log in user with password
				case 'P':
					{
					login_pw = TRUE;
					login_user = FALSE;

					if (argv[i][2])
						{
						CopyStringToBuffer(cmd_login, argv[i] + 2,
								sizeof(cmd_login) - 1);
						}
					else
						{
						i++;
						CopyStringToBuffer(cmd_login, argv[i],
								sizeof(cmd_login) - 1);
						}

					break;
					}

				// Run script
				case 'S':
					{
					if (argv[i][2])
						{
						CopyStringToBuffer(cmd_script, argv[i] + 2,
								sizeof(cmd_script) - 1);
						}
					else
						{
						i++;
						CopyStringToBuffer(cmd_login, argv[i],
								sizeof(cmd_script) - 1);
						}

					break;
					}

				// log in user with password
				case 'U':
					{
					if (!login_pw)
						{
						login_user = TRUE;

						if (argv[i][2])
							{
							CopyStringToBuffer(cmd_login, argv[i]+2,
									sizeof(cmd_login) - 1);
							}
						else
							{
							i++;
							CopyStringToBuffer(cmd_login, argv[i],
									sizeof(cmd_login) - 1);
							}
						}

					break;
					}

				// debug (verbose) mode
				case 'V':
					{
					TurnDebugOn();
					break;
					}

				case 'X':
					{
					batchmode = TRUE;
					break;
					}

				case '#':
					{
					cmd_mdata = atoi(argv[i]+2);
					break;
					}

				case '!':
					{
					slv_net = TRUE;

					if (argv[i][2])
						{
						strcpy(slv_node, argv[i]+2);
						}
					else
						{
						i++;
						strcpy(slv_node, argv[i]);
						}

					break;
					}

				default:
					printf(bn);
					printf(getmsg(753), argv[i]);
					printf(bn);
					// Fall through...

				case '?':
				case 'H':
					{
					usage();
					DeinitializeTimer();
					critical(FALSE);
					exit(200);
					}
				}
			}
		}
	}

void securityViolation(const char *pathname)
	{
	SetDoWhat(SECURITYVIOLATION);

	TI()CurrentUser->SetVerified(FALSE);

	char TrapString[256];
	sprintf(TrapString, getmsg(784), pathname,
			TI()CurrentUser->GetName());
	trap(TrapString, T_HACK);

	Message *Msg = new Message;

	if (Msg)
		{
		sprintf(TrapString, getmsg(1376), pathname,
				TI()CurrentUser->GetName(), cfg.Uuser_nym);
		Msg->SetText(TrapString);
		Msg->SetRoomNumber(AIDEROOM);
		systemMessage(Msg);
		delete Msg;
		}
	else
		{
		mPrintf(getmsg(188), getmsg(1365));
		}

	CRmPrintfCR(getmsg(759));

	CITWINDOW *w = ScreenSaver.IsOn() ? NULL :
			CitWindowsMsg(NULL, getmsg(1643));

	Hangup();

	if (w)
		{
		destroyCitWindow(w, FALSE);
		}
	}

const char *mkDblBck(const char *str, char *newstr)
	{
	char *wow = newstr;

	for (; *str; str++)
		{
		if (*str == '\\')
			{
			*(newstr++) = '\\';
			*newstr = '\\';
			}
		else if (*str == '\n')
			{
			*(newstr++) = '\\';
			*newstr = 'n';
			}
		else if (*str == '"')
			{
			*(newstr++) = '\\';
			*newstr = '"';
			}
		else if (*str == '\t')
			{
			*(newstr++) = '\\';
			*newstr = 't';
			}
		else if (*str == '\r')
			{
			*(newstr++) = '\\';
			*newstr = 'r';
			}
		else if (*str == '\f')
			{
			*(newstr++) = '\\';
			*newstr = 'f';
			}
		else if (*str == '\b')
			{
			*(newstr++) = '\\';
			*newstr = 'b';
			}
		else if (*str == '^')
			{
			*(newstr++) = '\\';
			*newstr = '^';
			}
		else if (*str == 127)
			{
			*(newstr++) = '^';
			*newstr = '?';
			}
		else
			{
			if (*str < 32 && *str > 0)
				{
				*(newstr++) = '^';
				*newstr = *str + '@';
				}
			else
				{
				*newstr = *str;
				}
			}

		newstr++;
		}

	*newstr = 0;
	return (wow);
	}

Bool TurnDebugOn(void)
	{
	if (!debug)
		{
		debug = read_db_messages();
		}

	return (debug);
	}

Bool TurnDebugOff(void)
	{
	if (debug)
		{
		debug = FALSE;
		dump_db_messages();
		}

	return (!debug);
	}

void UserHasTimedOut(void)
	{
	time(&TI()LastActiveTime);

	TI()OC.Echo = BOTH;
	setio();

	int i;

	if (cfg.sleepcount)
		{
		putWord((uchar *) getmsg(790));

		int i2;
		long t;

		time(&t);
		i2 = -1;

		do
			{
			i = cfg.sleepcount - (int) (time(NULL) - t);

			if (i != i2)
				{
				if (cfg.countbeep)
					{
					mPrintf("\a");
					}

				mPrintf(getmsg(1454), i);
				i2 = i;
				}

			if (BBSCharReady())
				{
				i2 = iCharNE();
				break;
				}
			} while (i > 0);
		}
	else
		{
		i = 0;
		}

	if (i <= 0)
		{
		char tmp[80];
		sprintf(tmp, pcts, cfg.sleepprompt);
		mFormat(tmp);
		doCR();

		if (onConsole)
			{
			TI()whichIO = MODEM;
			setio();
			}

		CITWINDOW *w = ScreenSaver.IsOn() ? NULL :
				CitWindowsMsg(NULL, getmsg(1643));

		Hangup();

		if (w)
			{
			destroyCitWindow(w, FALSE);
			}
		}
	}

static struct
	{
	char Digit;
	int Value;
	} RomanInputTable[] =
	{
	{ 'I', 1 },
	{ 'V', 5 },
	{ 'X', 10 },
	{ 'L', 50 },
	{ 'C', 100 },
	{ 'D', 500 },
	{ 'M', 1000 },
	};

static Bool GetNextRomanDigit(const char *Roman, int *Pos, long *Value)
	{
	if (Roman[*Pos])
		{
		long Multiplier = 1;

		if (Roman[*Pos] == '=')
			{
			Multiplier = 1000000l;
			(*Pos)++;
			}
		else if (Roman[*Pos] == '-')
			{
			Multiplier = 1000l;
			(*Pos)++;
			}

		for (int i = 0; i < sizeof(RomanInputTable) / sizeof(RomanInputTable[0]);
				i++)
			{
			if (RomanInputTable[i].Digit == toupper(Roman[*Pos]))
				{
				(*Pos)++;
				*Value = RomanInputTable[i].Value * Multiplier;
				return (TRUE);
				}
			}
		}

	*Value = 0;
	return (FALSE);
	}

long RomanInput(const char *Roman)
	{
	int Pos = 0;
	long Value, Total = 0;

	while (GetNextRomanDigit(Roman, &Pos, &Value))
		{
		int CopyPos = Pos;

		long Value2;
		GetNextRomanDigit(Roman, &CopyPos, &Value2);

		if (Value < Value2)
			{
			Total -= Value;
			}
		else
			{
			Total += Value;
			}
		}

	return (Total);
	}

static struct
	{
	long Breakoff;
	char Char1;
	char Char2;
	long Offset;
	} RomanTable[] =
	{
	{ 1000000000l, '=', 'M', -1000000000l}, // One billion
	{  900000000l, '=', 'C', +100000000l},  // Nine hundred million
	{  500000000l, '=', 'D', -500000000l},  // Five hundred million
	{  400000000l, '=', 'C', +100000000l},  // Four hundred million
	{  100000000l, '=', 'C', -100000000l},  // One hundred million
	{	90000000l, '=', 'X', +10000000l},   // Ninty million
	{	50000000l, '=', 'L', -50000000l},   // Fifty million
	{	40000000l, '=', 'X', +10000000l},   // Forty million
	{	10000000l, '=', 'X', -10000000l},   // Ten million
	{	 9000000l, '-', 'M', +1000000l},    // Nine million
	{	 5000000l, '=', 'V', -5000000l},    // Five million
	{	 4000000l, '-', 'M', +1000000l},    // Four million
	{	 1000000l, '-', 'M', -1000000l},    // One million
	{	  900000l, '-', 'C', +100000l},     // Nine hundred thousand
	{	  500000l, '-', 'D', -500000l},     // Five hundred thousand
	{	  400000l, '-', 'C', +100000l},     // Four hundred thousand
	{	  100000l, '-', 'C', -100000l},     // One hundred thousand
	{	   90000l, '-', 'X', +10000l},      // Ninty thousand
	{	   50000l, '-', 'L', -50000l},      // Fifty thousand
	{	   40000l, '-', 'X', +10000l},      // Forty thousand
	{	   10000l, '-', 'X', -10000l},      // Ten thousand
	{		9000l, 'M', 0,   +1000l},       // Nine thousand
	{		5000l, '-', 'V', -5000l},       // Five thousand
	{		4000l, 'M', 0,   +1000l},       // Four thousand
	{		1000l, 'M', 0,   -1000l},       // One thousand
	{		 900l, 'C', 0,   +100l},        // Nine hundred
	{		 500l, 'D', 0,   -500l},        // Five hundred
	{		 400l, 'C', 0,   +100l},        // Four hundred
	{		 100l, 'C', 0,   -100l},        // One hundred
	{		  90l, 'X', 0,   +10l},         // Ninty
	{		  50l, 'L', 0,   -50l},         // Fifty
	{		  40l, 'X', 0,   +10l},         // Forty
	{		  10l, 'X', 0,   -10l},         // Ten
	{		   9l, 'I', 0,   +1l},          // Nine
	{		   5l, 'V', 0,   -5l},          // Five
	{		   4l, 'I', 0,   +1l},          // Four
	{		   1l, 'I', 0,   -1l},          // One
	};

const char *RomanOutput(long Number)
	{
	// The longest number created with roman numerals is 1,888,888,888
	// (=M=D=C=C=C=L=X=X=X=V-M-M-M-D-C-C-C-L-X-X-X-VMMMDCCCLXXXVIII),
	// which is fifty-nine characters long. Add "Negative " and you get
	// sixty-eight. Add a few for translations of "Negative " and you get
	// the 79 that we are allowing ourselves.

	static char Result[80];
	int Pos;

	if (Number < 0)
		{
		Number = -Number;

		strcpy(Result, getmsg(1698));
		Pos = strlen(Result);
		}
	else
		{
		Pos = 0;
		}

	while (Number > 0)
		{
		for (int i = 0; Number < RomanTable[i].Breakoff; i++);

		Result[Pos++] = RomanTable[i].Char1;
		if (RomanTable[i].Char2)
			{
			Result[Pos++] = RomanTable[i].Char2;
			}

		Number += RomanTable[i].Offset;
		}

	if (!Pos)
		{
		CopyStringToBuffer(Result, getmsg(1699), sizeof(Result) - 1);
		}
	else
		{
		Result[Pos] = 0;
		}

	return (Result);
	}
