/*
**	Main.c
**
**	Program main routines and event loop
**
**	Copyright © 1990-1996 by Olaf `Olsen' Barthel
**		All Rights Reserved
**
**	:ts=4
*/

#ifndef _GLOBAL_H
#include "Global.h"
#endif

	/* Argument vectors offsets. */

enum	{
			ARG_WINDOW,ARG_PUBSCREEN,ARG_STARTUP,ARG_PORTNAME,ARG_SETTINGS,ARG_UNIT,ARG_DEVICE,
			ARG_NEW,ARG_SYNC,ARG_QUIET,ARG_BEHIND,ARG_DEBUG,ARG_LANGUAGE,ARG_PHONEBOOK,ARG_AUTODIAL,
			ARG_AUTOEXIT,ARG_WINDOWTITLE,

			ARG_COUNT
		};

	/* The shell argument template and the corresponding help message. */

STATIC STRPTR 		ArgTemplate =	"WINDOW/K,PUBSCREEN/K,STARTUP/K,PORTNAME/K,"
									"SETTINGS/K,UNIT/K/N,DEVICE/K,NEW/S,SYNC/S,"
									"QUIET/S,BEHIND/S,DEBUG/S,LANGUAGE/K,PHONEBOOK/K,"
									"AUTODIAL/S,AUTOEXIT/S,WINDOWTITLE/K";

STATIC STRPTR		ArgHelp =		"\nUsage: term [WINDOW <Name>] [PUBSCREEN <Name>] [STARTUP <File name>]\n"
									"            [SETTINGS <File or path name>] [UNIT <Number>] [DEVICE <Name>]\n"
									"            [NEW] [SYNC] [QUIET] [BEHIND] [LANGUAGE <Name>] [PHONEBOOK <File name>]\n"
									"            [AUTODIAL] [AUTOEXIT] [WINDOWTITLE <Title>]\n"
									"\n"
									"      Window = Output window specifier\n"
									"   PubScreen = Name of public screen to open window upon\n"
									"     Startup = ARexx script file to run on startup\n"
									"    Settings = Main configuration file name or path name to search for it\n"
									"        Unit = Serial device driver unit number\n"
									"      Device = Serial device driver name\n"
									"         New = Spawn a new `term' process\n"
									"        Sync = Keep links to Shell environment\n"
									"       Quiet = Start iconified\n"
									"      Behind = Open screen behind all other screens, don't activate the window\n"
									"   Phonebook = Name of phonebook file to load\n"
									"    Autodial = Dial phonebook entries which are marked for autodialing\n"
									"    Autoexit = Exit `term' after dialing all autodial entries\n"
									" Windowtitle = Display this text as the terminal window title\n"
									"    Language = Language to use for the user interface\n\n";

	/* Local config path variable. */

STATIC STRPTR		ConfigPath;
STATIC UBYTE		ThePath[MAX_FILENAME_LENGTH];

	/* Startup file name. */

STATIC UBYTE		StartupFile[MAX_FILENAME_LENGTH];

	/* Console output window specifier. */

STATIC UBYTE		WindowName[256];

	/* Did we hang up the line? */

STATIC BOOL			HungUp;

	/* Go on redialing? */

STATIC BOOL			KeepRedialing;
STATIC BOOL			AutoDial;
STATIC BOOL			AutoExit;

	/* Display the connection cost? */

STATIC BOOL			DisplayHangup;
STATIC ULONG		DisplayPay;

	/* Poll OwnDevUnit.library for the device to become available again? */

STATIC BOOL			PollODU;
STATIC UWORD		PollODUCount;

	/* Check the menu for items to dial? */

STATIC BOOL			CheckDialMenu;

	/* Local routines */

STATIC BOOL HandleInput(VOID);
STATIC VOID ClearDialMenu(VOID);
STATIC BOOL DialMenuToDialList(VOID);
STATIC VOID HandleMenu(ULONG Code,ULONG Qualifier);
STATIC VOID HandleIconify(VOID);
STATIC VOID HandleOnlineCleanup(BOOL Hangup);
STATIC VOID HandleFlowChange(VOID);
STATIC VOID FullHangup(BOOL ForceIt);
STATIC BOOL AskDial(struct Window *Parent);
STATIC VOID GoOnline(VOID);
STATIC VOID ReadyToDial(struct DataDialMsg *DialMsg);
STATIC VOID HandleUpload(UBYTE Type);
STATIC VOID LocalReleaseSerial(VOID);
STATIC VOID HandleKeyboardInput(UBYTE Char,UWORD Code,ULONG Qualifier,STRPTR InputBuffer,LONG Len);

	/* Main():
	 *
	 *	This is our main entry point.
	 */

LONG
Main()
{
	LONG Error = 0;

		/* Are we running as a child of Workbench? */

	ThisProcess = (struct Process *)FindTask(NULL);

	if(ThisProcess->pr_CLI)
		WBenchMsg = NULL;
	else
	{
		WaitPort(&ThisProcess->pr_MsgPort);

		WBenchMsg = (struct WBStartup *)GetMsg(&ThisProcess->pr_MsgPort);
	}

		/* Now try to open dos.library and utility.library and
		 * proceed to examine the startup parameters.
		 */

	DOSBase		= (struct DosLibrary *)OpenLibrary("dos.library",0);
	UtilityBase	= OpenLibrary("utility.library",0);

	if(DOSBase && UtilityBase)
	{
		struct RDArgs *ArgsPtr;
		STRPTR *ArgArray;
		BOOL LaunchNew;

			/* Reset these first. */

		ArgsPtr		= NULL;
		ArgArray	= NULL;
		LaunchNew	= TRUE;

			/* We were called from Shell. */

		if(ThisProcess->pr_CLI)
		{
				/* No home directory to return to. */

			WBenchLock = NULL;

				/* Use the ReadArgs parser, allocate the
				 * argument vectors...
				 */

			if(ArgArray = (STRPTR *)AllocVec(sizeof(STRPTR) * (ARG_COUNT),MEMF_ANY|MEMF_CLEAR))
			{
				if(ArgsPtr = (struct RDArgs *)AllocDosObject(DOS_RDARGS,TAG_DONE))
				{
					ArgsPtr->RDA_ExtHelp = ArgHelp;

						/* Parse the args (if any). */

					if(ReadArgs(ArgTemplate,(LONG *)ArgArray,ArgsPtr))
					{
							/* Pop a running `term' to the front? */

						if(!ArgArray[ARG_NEW])
							LaunchNew = FALSE;

							/* For debugging purposes; not used yet. */

						if(ArgArray[ARG_DEBUG])
							DebugFlag = TRUE;

							/* Special language requested? */

						if(ArgArray[ARG_LANGUAGE])
							LimitedStrcpy(sizeof(Language),Language,ArgArray[ARG_LANGUAGE]);

							/* Are we to use a special settings path? */

						if(ArgArray[ARG_SETTINGS])
						{
							ConfigPath = ThePath;

							LimitedStrcpy(sizeof(ThePath),ThePath,ArgArray[ARG_SETTINGS]);
						}

							/* Are we to use a special ARexx host port name? */

						if(ArgArray[ARG_PORTNAME])
							LimitedStrcpy(sizeof(RexxPortName),RexxPortName,ArgArray[ARG_PORTNAME]);

							/* Are we to use a special output window name? */

						if(ArgArray[ARG_WINDOW])
							LimitedStrcpy(sizeof(WindowName),WindowName,ArgArray[ARG_WINDOW]);

							/* Are we to run an ARexx script on startup? */

						if(ArgArray[ARG_STARTUP])
							LimitedStrcpy(sizeof(StartupFile),StartupFile,ArgArray[ARG_STARTUP]);

							/* Load a special phonebook file */

						if(ArgArray[ARG_PHONEBOOK])
							LimitedStrcpy(sizeof(LastPhone),LastPhone,ArgArray[ARG_PHONEBOOK]);

						if(ArgArray[ARG_AUTODIAL])
							AutoDial = TRUE;

						if(ArgArray[ARG_AUTOEXIT])
							AutoExit = TRUE;

							/* Are we to open a window on a public screen? */

						if(ArgArray[ARG_PUBSCREEN])
							LimitedStrcpy(sizeof(SomePubScreenName),SomePubScreenName,ArgArray[ARG_PUBSCREEN]);

							/* Are we to use a special device? */

						if(ArgArray[ARG_DEVICE])
						{
							LimitedStrcpy(sizeof(NewDevice),NewDevice,ArgArray[ARG_DEVICE]);

							UseNewDevice = TRUE;
						}

							/* Are we to use a special unit number? */

						if(ArgArray[ARG_UNIT])
						{
							NewUnit = *(LONG *)ArgArray[ARG_UNIT];

							UseNewUnit = TRUE;
						}

							/* Are we to start up iconified? */

						if(ArgArray[ARG_QUIET])
						{
							if(!StartupFile[0])
								DoIconify = TRUE;
						}

							/* Hide the screen and don't activate the window? */

						if(ArgArray[ARG_BEHIND])
							KeepQuiet = TRUE;

							/* Use a special window title? */

						if(ArgArray[ARG_WINDOWTITLE])
						{
							CopyMem(ArgArray[ARG_WINDOWTITLE],WindowTitle,79);
							WindowTitle[79] = 0;
						}
						else
							WindowTitle[0] = 0;
					}
					else
						Error = IoErr();
				}
				else
					Error = IoErr();
			}
			else
				Error = IoErr();
		}
		else
		{
			BOOL ScrewThePath = TRUE;

				/* Change to the program's home directory. */

			WBenchLock = CurrentDir(WBenchMsg->sm_ArgList->wa_Lock);

				/* Open icon.library, we want to take a
				 * look at the icon.
				 */

			if(IconBase = OpenLibrary("icon.library",0))
			{
				struct DiskObject *Icon;

					/* Try to read the icon file. */

				if(Icon = GetProgramIcon())
				{
					STRPTR Type;

					if(FindToolType((UBYTE **)Icon->do_ToolTypes,"DEBUG"))
						DebugFlag = TRUE;

					if(FindToolType((UBYTE **)Icon->do_ToolTypes,"NOPATHFIX"))
						ScrewThePath = FALSE;

						/* Look for a `Language' tooltype. */

					if(Type = FindToolType((UBYTE **)Icon->do_ToolTypes,"LANGUAGE"))
						LimitedStrcpy(sizeof(Language),Language,Type);

						/* Look for a `Settings' tooltype. */

					if(ConfigPath = FindToolType((UBYTE **)Icon->do_ToolTypes,"SETTINGS"))
					{
							/* Remember the path and continue. */

						LimitedStrcpy(sizeof(ThePath),ThePath,ConfigPath);

						ConfigPath = ThePath;
					}

						/* Look for a `Phonebook' tooltype. */

					if(Type = FindToolType((UBYTE **)Icon->do_ToolTypes,"PHONEBOOK"))
						LimitedStrcpy(sizeof(LastPhone),LastPhone,Type);

					if(FindToolType((UBYTE **)Icon->do_ToolTypes,"AUTODIAL"))
						AutoDial = TRUE;

					if(FindToolType((UBYTE **)Icon->do_ToolTypes,"AUTOEXIT"))
						AutoExit = TRUE;

						/* Look for a `Portname' tooltype. */

					if(Type = FindToolType((UBYTE **)Icon->do_ToolTypes,"PORTNAME"))
						LimitedStrcpy(sizeof(RexxPortName),RexxPortName,Type);
					else
						RexxPortName[0] = 0;

						/* Look for a `Window' tooltype. */

					if(Type = FindToolType((UBYTE **)Icon->do_ToolTypes,"WINDOW"))
						LimitedStrcpy(sizeof(WindowName),WindowName,Type);

						/* Look for a `Windowtitle' tooltype. */

					if(Type = FindToolType((UBYTE **)Icon->do_ToolTypes,"WINDOWTITLE"))
						LimitedStrcpy(sizeof(WindowTitle),WindowTitle,Type);
					else
						WindowTitle[0] = 0;

						/* Look for a `Pubscreen' tooltype. */

					if(Type = FindToolType((UBYTE **)Icon->do_ToolTypes,"PUBSCREEN"))
						LimitedStrcpy(sizeof(SomePubScreenName),SomePubScreenName,Type);
					else
						SomePubScreenName[0] = 0;

						/* Look for a `Startup' tooltype. */

					if(Type = FindToolType((UBYTE **)Icon->do_ToolTypes,"STARTUP"))
						LimitedStrcpy(sizeof(StartupFile),StartupFile,Type);
					else
						StartupFile[0] = 0;

						/* Look for a `Device' tooltype. */

					if(Type = FindToolType((UBYTE **)Icon->do_ToolTypes,"DEVICE"))
					{
						if(Type[0])
						{
							LimitedStrcpy(sizeof(NewDevice),NewDevice,Type);

							UseNewDevice = TRUE;
						}
					}

						/* Look for a `Unit' tooltype. */

					if(Type = FindToolType((UBYTE **)Icon->do_ToolTypes,"UNIT"))
					{
						if(Type[0])
						{
							NewUnit = Atol(Type);

							UseNewUnit = TRUE;
						}
					}

						/* Look for a `Quiet' tooltype. */

					if(FindToolType((UBYTE **)Icon->do_ToolTypes,"QUIET"))
					{
						if(!StartupFile[0])
							DoIconify = TRUE;
					}

						/* Look for a `Behind' tooltype. */

					if(FindToolType((UBYTE **)Icon->do_ToolTypes,"BEHIND"))
						KeepQuiet = TRUE;

						/* Free the icon. */

					FreeDiskObject(Icon);
				}

				CloseLibrary(IconBase);
				IconBase = NULL;
			}

				/* Try to create a local CLI structure so
				 * Shell commands will receive a valid
				 * search path list.
				 */

			if(ScrewThePath)
				AttachCLI(WBenchMsg);
		}

			/* Show the error message; Error can be non-zero only if
			 * `term' was started from shell.
			 */

		if(Error)
			PrintFault(Error,"term");
		else
		{
				/* If there is already a `term' program running, pop its window to the front. */

			if(!LaunchNew)
			{
				struct TermPort *TermPort;

					/* If no other program is running, don't bother. */

				Forbid();

				if(!(TermPort = TermPort = (struct TermPort *)FindPort(TERMPORTNAME)))
					LaunchNew = TRUE;
				else
				{
						/* Open intuition and pop the window to the front. */

					if(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0))
					{
						if(TermPort->TopWindow)
							BumpWindow(TermPort->TopWindow);

						CloseLibrary((struct Library *)IntuitionBase);
						IntuitionBase = NULL;
					}
				}

				Permit();
			}

				/* Do something sensible? */

			if(LaunchNew)
			{
				BYTE OldTaskPri = ThisProcess->pr_Task.tc_Node.ln_Pri;

				if(StackSize(NULL) < 16384)
				{
					LONG Success;

					if(!StackCall(&Success,16384,0,(STACKCALL)HandleInput))
						Error = ERROR_NO_FREE_STORE;
				}
				else
				{
					if(!HandleInput())
						Error = ERROR_NO_FREE_STORE;
				}

				CloseAll();

				SetTaskPri((struct Task *)ThisProcess,OldTaskPri);
			}
		}

			/* Now clean up any allocated data. */

		if(ArgsPtr)
		{
			FreeArgs(ArgsPtr);
			FreeDosObject(DOS_RDARGS,ArgsPtr);
		}

		FreeVec(ArgArray);

			/* Return to the old drawer. */

		if(WBenchMsg)
			CurrentDir(WBenchLock);
	}

		/* Close the libraries. */

	CloseLibrary((struct Library *)DOSBase);
	CloseLibrary(UtilityBase);

		/* If run from Workbench, return the startup message. */

	if(WBenchMsg)
	{
		Forbid();

		ReplyMsg((struct Message *)WBenchMsg);
	}

	if(Error)
		return(RETURN_ERROR);
	else
		return(RETURN_OK);
}

#ifdef BETA
STRPTR Warning =
	"\f  Just to make sure you know what you are using:\r\n"
	"\r\n"
	"     \033#6This really is a\r\n"
	"        \033[5m\033#3BETA TEST\r\n"
	"        \033#4BETA TEST\033[0m\r\n"
	"         \033#6release!\r\n"
	"\033#5\r\n"
	"                   \033[4mDISCLAIMER:\033[0m\r\n"
	"\r\n"
	"This  product  is  meant  for educational purposes\r\n"
	"only.   If   condition   persists,   consult  your\r\n"
	"physician.  Edited  for  television. See label for\r\n"
	"sequence.  Avoid  contact  with  skin. No purchase\r\n"
	"necessary.  Use  only in well-ventilated area. Not\r\n"
	"recommended  for  children.  No  anchovies  unless\r\n"
	"otherwise  specified.  Driver does not carry cash.\r\n"
	"This supersedes all previous notices.\r\n";
#endif	/* BETA */

	/* HandleInput():
	 *
	 *	This is our main input loop (check window & serial).
	 */

STATIC BOOL
HandleInput()
{
	STRPTR			 Result;
	SENDLINE		 OriginalSendLine;
	PhonebookHandle	*PhoneHandle;

	ThisProcess = (struct Process *)FindTask(NULL);

		/* Open the resources we need. */

	if(Result = OpenAll(ConfigPath))
	{
		if(IntuitionBase && Result[0])
			ShowRequest(NULL,LocaleString(MSG_GLOBAL_TERM_HAS_A_PROBLEM_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),Result);

		return(FALSE);
	}

		/* Store the output window name. */

	if(WindowName[0])
		LimitedStrcpy(sizeof(Config->MiscConfig->WindowName),Config->MiscConfig->WindowName,WindowName);

		/* Tell the user what he probably doesn't know yet. */

	if(TermVersion > Config->SerialConfig->LastVersionSaved || (TermVersion == Config->SerialConfig->LastVersionSaved && TermRevision > Config->SerialConfig->LastRevisionSaved))
	{
		BlockWindows();

		ScreenToFront(Window->WScreen);

		DisplayBeep(Window->WScreen);
		DisplayBeep(Window->WScreen);

		ShowInfo(Window,LocaleString(MSG_ATTENTION_PLEASE_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),LocaleString(MSG_REMINDER_TXT));

		ReleaseWindows();
	}

		/* Give a hint. */

	if(Config->MiscConfig->ProtectiveMode && !FirstInvocation)
	{
		BlockWindows();

		if(Config->SerialConfig->BaudRate >= 4800 && Config->SerialConfig->HandshakingProtocol == HANDSHAKING_NONE && !Config->SerialConfig->DirectConnection)
		{
			ScreenToFront(Window->WScreen);

			if(ShowRequest(Window,LocaleString(MSG_NO_RTSCTS_TXT),LocaleString(MSG_GLOBAL_YES_NO_TXT),Config->SerialConfig->BaudRate))
			{
				SaveConfig(Config,PrivateConfig);

				Config->SerialConfig->HandshakingProtocol = HANDSHAKING_RTSCTS_DSR;

				ConfigSetup();
			}
		}

		if(Config->SerialConfig->BaudRate >= 4800 && Config->ModemConfig->ConnectAutoBaud)
		{
			ScreenToFront(Window->WScreen);

			if(ShowRequest(Window,LocaleString(MSG_AUTOBAUD_TXT),LocaleString(MSG_GLOBAL_YES_NO_TXT)))
			{
				SaveConfig(Config,PrivateConfig);

				Config->ModemConfig->ConnectAutoBaud = FALSE;

				ConfigSetup();
			}
		}

		ReleaseWindows();
	}

		/* Don't confuse the user yet, do it later ;-) */

	FirstInvocation = FALSE;

		/* Start the online timer if a carrier is present? */

	if(Config->SerialConfig->CheckCarrier && !Config->SerialConfig->DirectConnection)
	{
			/* Is the carrier signal present? */

		if(!(GetSerialStatus() & CIAF_COMCD))
			GoOnline();
	}

		/* Start up iconified? */

	if(DoIconify)
	{
		HandleIconify();

		DoIconify = FALSE;
	}

		/* If we came out of iconified mode, check if were told to quit. */

	if(!MainTerminated)
	{
		if(!KeepQuiet)
			BumpWindow(Window);

			/* Set up the public screen data. */

		PubScreenStuff();

			/* Change program priority. */

		SetTaskPri((struct Task *)ThisProcess,(LONG)Config->MiscConfig->Priority);

		BlockWindows();

			/* Load the phone book. */

		if(PhoneHandle = LoadPhonebook(LastPhone))
		{
			DeletePhonebook(GlobalPhoneHandle);
			GlobalPhoneHandle = PhoneHandle;

			strcpy(Config->PhonebookFileName,LastPhone);
		}

			/* Build new menu strip. */

		if(!AttachMenu(NULL))
		{
			if(IntuitionBase)
				ShowRequest(NULL,LocaleString(MSG_GLOBAL_TERM_HAS_A_PROBLEM_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),LocaleString(MSG_TERMINIT_FAILED_TO_CREATE_MENUS_TXT));

			return(FALSE);
		}
		else
		{
			if(Online)
				SetDialMenu(FALSE);
		}

			/* Show our business card. */

		if(!StartupFile[0] && !Config->CommandConfig->StartupMacro[0] && !KeepQuiet && !(GlobalPhoneHandle->AutoDial || AutoDial))
			ShowAbout(TRUE);

		ReleaseWindows();

			/* Don't do anything silly. */

		KeepQuiet = FALSE;

		LogAction(LocaleString(MSG_TERMMAIN_LOGMSG_PROGRAM_STARTED_TXT),TermName,TermDate);

			/* Initialize the modem. */

		OriginalSendLine = ChangeSendLine(SendLineDial);

		SerialCommand(Config->ModemConfig->ModemInit);

			/* Execute the startup macro (if any). */

		if(Config->CommandConfig->StartupMacro[0])
			SerialCommand(Config->CommandConfig->StartupMacro);

		RestoreSendLine(SendLineDial,OriginalSendLine);

		#ifdef BETA
		ConProcess(Warning,strlen(Warning));
		#endif	/* BETA */

			/* Check if we should dial some entries from the phonebook */
			/* right upon startup */

		if(GlobalPhoneHandle->AutoDial || AutoDial)
		{
			PhoneEntry **Phonebook;
			BOOL DidSomething;
			LONG i;

			Phonebook = GlobalPhoneHandle->Phonebook;
			DidSomething = FALSE;

			for(i = 0 ; i < GlobalPhoneHandle->NumPhoneEntries ; i++)
			{
				if(Phonebook[i]->Header->AutoDial && GlobalPhoneHandle->Phonebook[i]->Header->Number[0])
				{
					MarkDialEntry(GlobalPhoneHandle,Phonebook[i]);

					DidSomething = TRUE;
				}
			}

			if(DidSomething)
			{
				if(AddAllDialEntries(GlobalPhoneHandle))
				{
					DoDial = DIAL_LIST;

					if(GlobalPhoneHandle->AutoExit || AutoExit)
						KeepRedialing = TRUE;
				}
			}
		}
		else
		{
			PhoneEntry **Phonebook;
			LONG HowMany;
			LONG i;

			Phonebook = GlobalPhoneHandle->Phonebook;
			HowMany = 0;

			for(i = 0 ; i < GlobalPhoneHandle->NumPhoneEntries ; i++)
			{
				if(Phonebook[i]->Header->Marked > 0)
					HowMany++;
			}

			if(HowMany > 0)
			{
				LONG j,Counter,Which;

				for(j = 0 ; j < HowMany ; j++)
				{
					Counter	= Which = -1;

					for(i = 0 ; i < GlobalPhoneHandle->NumPhoneEntries ; i++)
					{
						if(Phonebook[i]->Header->Marked > 0)
						{
							if(Counter == -1 || Phonebook[i]->Header->Marked <= Counter)
							{
								Counter	= Phonebook[i]->Header->Marked;
								Which	= i;
							}
						}
					}

					if(Which != -1)
					{
						MarkDialEntry(GlobalPhoneHandle,Phonebook[Which]);
						Phonebook[Which]->Header->Marked = 0;
					}
				}

				AddAllDialEntries(GlobalPhoneHandle);
			}
		}

			/* Process the startup file if any. */

		if(StartupFile[0])
			ActivateJob(MainJobQueue,StartupFileJob);

			/* Go into input loop... */

		do
		{
				/* Dial the list */

			if(DoDial != DIAL_IGNORE)
			{
				DoDial = DIAL_IGNORE;

				if(Online && !IsListEmpty(&RexxDialMsgList))
					DispatchRexxDialMsgList(FALSE);
				else
				{
					BlockWindows();

					ClearDialMenu();

					do
					{
						if(!DialPanel(GlobalPhoneHandle))
						{
							KeepRedialing = FALSE;
							break;
						}
					}
					while(PhonePanel(NULL));

					if(!GlobalPhoneHandle->AutoExit)
						KeepRedialing = FALSE;

					SetRedialMenu();

					ReleaseWindows();
				}
			}

				/* Process the job queue. */

			ProcessJobQueue(MainJobQueue);

				/* Is that it? */

			if(MainTerminated)
			{
				LONG Complaint;

					/* Check if there are ARexx programs running in the background. */

				if(GetLaunchCounter() > 0)
					Complaint = MSG_TERMMAIN_CANNOT_QUIT_YET_TXT;
				else
				{
						/* If we are about to quit, try to close the screen. */

					if(!DeleteDisplay())
						Complaint = MSG_TERMMAIN_CANNOT_CLOSE_SCREEN_YET_TXT;
					else
						Complaint = 0;
				}

					/* Check if we must complain. */

				if(Complaint > 0)
				{
					BlockWindows();
					ShowRequest(Window,LocaleString(MSG_GLOBAL_TERM_HAS_A_PROBLEM_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),LocaleString(Complaint));
					ReleaseWindows();

					MainTerminated = FALSE;
				}
			}
			else
			{
					/* Make the user notice not too obvious events. */

				if(FlowInfo.Changed)
					HandleFlowChange();
			}
		}
		while(!MainTerminated);
	}

		/* Send the modem exit command. */

	SendLine = (SENDLINE)SendLineDial;
	SerialCommand(Config->ModemConfig->ModemExit);

		/* Stop I/O processing. */

	ClearSerial();

		/* Say goodbye. */

	LogAction(LocaleString(MSG_TERMMAIN_LOGMSG_PROGRAM_TERMINATED_TXT));
	Say(LocaleString(MSG_TERMMAIN_BYE_BYE_TXT));

	return(TRUE);
}

	/* SendInputTextBuffer(STRPTR Buffer,LONG Len,BOOL Bell,BOOL ConvertLF):
	 *
	 *	Transmit text the user typed or pasted via the
	 *	clipboard.
	 */

VOID
SendInputTextBuffer(STRPTR Buffer,LONG Len,BOOL Bell,BOOL ConvertLF)
{
	LONG Mask;
	UBYTE c;

	if(Config->SerialConfig->StripBit8)
		Mask = 0x7F;
	else
		Mask = 0xFF;

	while(Len--)
	{
		switch(CharType[c = (*Buffer++) & Mask])
		{
			case CHAR_ENTER:

				if(Get_xOFF())
				{
					if(Bell)
						BellSignal();
				}
				else
				{
					if(ConvertLF)
					{
						switch(Config->TerminalConfig->SendCR)
						{
							case EOL_LF:

								SerWrite("\n",1);
								break;

							case EOL_CR:

								SerWrite("\r",1);
								break;

							case EOL_LFCR:

								SerWrite("\n\r",2);
								break;

							case EOL_CRLF:

								SerWrite("\r\n",2);
								break;
						}
					}
					else
					{
						switch(Config->TerminalConfig->SendLF)
						{
							case EOL_LF:

								SerWrite("\n",1);
								break;

							case EOL_CR:

								SerWrite("\r",1);
								break;

							case EOL_LFCR:

								SerWrite("\n\r",2);
								break;

							case EOL_CRLF:

								SerWrite("\r\n",2);
								break;
						}
					}
				}

				break;

			case CHAR_RETURN:

				if(Get_xOFF())
				{
					if(Bell)
						BellSignal();
				}
				else
				{
					switch(Config->TerminalConfig->SendCR)
					{
						case EOL_LF:

							SerWrite("\n",1);
							break;

						case EOL_CR:

							SerWrite("\r",1);
							break;

						case EOL_LFCR:

							SerWrite("\n\r",2);
							break;

						case EOL_CRLF:

							SerWrite("\r\n",2);
							break;
					}
				}

				break;

				/* Restart in/output. */

			case CHAR_XON:

				if(Config->SerialConfig->xONxOFF)
					Clear_xOFF();

				if(Config->SerialConfig->PassThrough)
					SerWrite(&c,1);

				break;

				/* Stop in/output. */

			case CHAR_XOFF:

				if(Config->SerialConfig->xONxOFF)
					Set_xOFF();

				if(Config->SerialConfig->PassThrough)
					SerWrite(&c,1);

				break;

				/* Any other character. */

			case CHAR_VANILLA:

				if(Get_xOFF())
				{
					if(Bell)
						BellSignal();
				}
				else
				{
					if(Config->TerminalConfig->FontMode == FONT_IBM)
					{
							/* Convert special
							 * Amiga characters into
							 * alien IBM dialect.
							 */

						if(IBMConversion[c])
							SerWrite(&IBMConversion[c],1);
						else
							SerWrite(&c,1);
					}
					else
						SerWrite(&c,1);
				}

				break;
		}
	}
}

STATIC VOID
ClearDialMenu()
{
	if(Menu)
	{
		struct Menu	*ThisMenu;
		struct MenuItem	*Item;

		ClearMenuStrips();

		for(ThisMenu = Menu ; ThisMenu ; ThisMenu = ThisMenu->NextMenu)
		{
			if(Item = ThisMenu->FirstItem)
			{
				do
				{
					if((ULONG)GTMENUITEM_USERDATA(Item) >= DIAL_MENU_LIMIT)
					{
						Item->Flags &= ~CHECKED;

						if(Item->SubItem)
						{
							struct MenuItem *SubItem = Item->SubItem;

							do
								SubItem->Flags &= ~CHECKED;
							while(SubItem = SubItem->NextItem);
						}
					}
				}
				while(Item = Item->NextItem);
			}
		}

		ResetMenuStrips(Menu);
	}
}

STATIC BOOL
DialMenuToDialList()
{
	ClearDialMenu();

	if(QuickDialMax > 0)
	{
		BOOL GotAll;
		LONG i;

		DeleteDialList(GlobalPhoneHandle);

		GotAll = TRUE;

		for(i = 0 ; i < QuickDialMax ; i++)
		{
			if(QuickDialTable[i] != -1)
			{
				if(GotAll)
					GotAll = AddDialEntry(GlobalPhoneHandle,GlobalPhoneHandle->Phonebook[QuickDialTable[i] - DIAL_MENU_LIMIT],NULL);

				QuickDialTable[i] = -1;
			}
		}

		QuickDialMax = 0;

		if(GotAll)
			return(TRUE);

		DeleteDialList(GlobalPhoneHandle);
	}

	return(FALSE);
}

	/* HandleMenu(ULONG Code,ULONG Qualifier):
	 *
	 *	Skip along the number of selected menu items and
	 *	handle the associated functions.
	 */

STATIC VOID
HandleMenu(ULONG Code,ULONG Qualifier)
{
	struct MenuItem *MenuItem;

		/* Check until the last menuitem has been
		 * processed.
		 */

	while(Code != MENUNULL)
	{
			/* Pick up the associated menu item. */

		if(MenuItem = ItemAddress(Menu,Code))
		{
			HandleMenuCode(MenuItem,(ULONG)GTMENUITEM_USERDATA(MenuItem),Qualifier);

			Code = MenuItem->NextSelect;
		}
		else
			break;
	}

		/* Enter the selected entries from the quick dial menu? */

	if(CheckDialMenu)
	{
		CheckDialMenu = FALSE;

		if(DialMenuToDialList())
			DoDial = DIAL_LIST;
	}
}

	/* HandleIconify():
	 *
	 *	Handle program iconification.
	 */

STATIC VOID
HandleIconify()
{
	BOOL Released = FALSE;

		/* Set the wait mouse pointer... */

	BlockWindows();

		/* Open workbench.library. */

	if(WorkbenchBase)
	{
			/* Open icon.library. */

		if(IconBase)
		{
			UBYTE LocalBuffer[MAX_FILENAME_LENGTH];
			struct DiskObject *Icon;

			strcpy(LocalBuffer,Config->PathConfig->DefaultStorage);

			if(AddPart(LocalBuffer,"term_SleepIcon",sizeof(LocalBuffer)))
				Icon = GetDiskObject(LocalBuffer);
			else
				Icon = NULL;

			if(!Icon)
				Icon = GetDiskObject("PROGDIR:term_SleepIcon");

			if(!Icon)
			{
				if(!(Icon = GetProgramIcon()))
					Icon = GetDefDiskObject(WBTOOL);
			}

				/* Did we get an icon? */

			if(Icon)
			{
				struct MsgPort *IconPort;

					/* Reset the icon type. */

				Icon->do_Type = NULL;

					/* Default icon position. */

				Icon->do_CurrentX = NO_ICON_POSITION;
				Icon->do_CurrentY = NO_ICON_POSITION;

					/* Create the Workbench reply port. */

				if(IconPort = CreateMsgPort())
				{
					struct AppIcon *AppIcon;

						/* Add the application icon. */

					if(AppIcon = AddAppIconA(0,0,TermIDString,IconPort,NULL,Icon,NULL))
					{
						struct AppMessage *AppMessage;

							/* Release the window. */

						Released = TRUE;
						ReleaseWindows();

							/* Close the display. full stop. */

						if(DeleteDisplay())
						{
							ULONG Signals;

								/* Reset and release the serial driver. */

							if(Config->MiscConfig->ReleaseDevice)
							{
								ClearSerial();
								DeleteSerial();
							}

								/* Wait for double-click. */

							IconTerminated = FALSE;

							do
							{
								Signals = Wait(PORTMASK(IconPort) | SIG_REXX | SIGBREAKF_CTRL_F);

									/* Received a double-click? */

								if(Signals & PORTMASK(IconPort))
								{
										/* Pick up application messages. */

									while(AppMessage = (struct AppMessage *)GetMsg(IconPort))
									{
										IconTerminated = TRUE;

										ReplyMsg((struct Message *)AppMessage);
									}
								}

									/* Wake up if ARexx command received. */

								if(Signals & SIG_REXX)
									while(RunJob(RexxJob));

									/* Generic wakeup signal. */

								if(Signals & SIGBREAKF_CTRL_F)
									IconTerminated = TRUE;

									/* Ready to leave? */

								if(IconTerminated)
								{
										/* Open the serial driver. */

									if(Config->MiscConfig->ReleaseDevice)
									{
										if(!CreateSerial(LocalBuffer,sizeof(LocalBuffer)))
										{
											switch(ShowRequest(NULL,LocaleString(MSG_GLOBAL_TERM_HAS_A_PROBLEM_TXT),LocaleString(MSG_TERMMAIN_ICONIFY_IGNORE_QUIT_TXT),LocalBuffer))
											{
												case 0:

													MainTerminated = TRUE;
													break;

												case 1:

													IconTerminated = FALSE;
													break;
											}
										}
									}
								}

									/* Still ready to leave? */

								if(IconTerminated && !MainTerminated)
								{
									STRPTR String;

										/* Create the display. */

									if(String = CreateDisplay(TRUE,FALSE))
									{
										switch(ShowRequest(NULL,LocaleString(MSG_GLOBAL_TERM_HAS_A_PROBLEM_TXT),LocaleString(MSG_TERMMAIN_ICONIFY_QUIT_TXT),String))
										{
											case 0:

												MainTerminated = TRUE;
												break;

											case 1:

													/* Reset and release the serial driver. */

												if(Config->MiscConfig->ReleaseDevice)
												{
													ClearSerial();
													DeleteSerial();
												}

												IconTerminated = FALSE;
												break;
										}
									}
									else
									{
										BumpWindow(Window);

										PubScreenStuff();
									}
								}
							}
							while(!IconTerminated);
						}
						else
						{
							BlockWindows();

							ShowRequest(Window,LocaleString(MSG_GLOBAL_TERM_HAS_A_PROBLEM_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),LocaleString(MSG_TERMMAIN_CANNOT_CLOSE_SCREEN_YET_TXT));

							ReleaseWindows();
						}

							/* Remove the application icon. */

						RemoveAppIcon(AppIcon);

							/* Reply pending messages. */

						while(AppMessage = (struct AppMessage *)GetMsg(IconPort))
							ReplyMsg((struct Message *)AppMessage);
					}
					else
						ShowRequest(Window,LocaleString(MSG_TERMMAIN_FAILED_TO_ADD_APPLICATION_ICON_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT));

					DeleteMsgPort(IconPort);
				}
				else
					ShowRequest(Window,LocaleString(MSG_GLOBAL_FAILED_TO_CREATE_MSGPORT_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT));

				FreeDiskObject(Icon);
			}
			else
				ShowRequest(Window,LocaleString(MSG_TERMMAIN_FAILED_TO_OPEN_TOOL_ICON_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT));
		}
		else
			ShowRequest(Window,LocaleString(MSG_TERMMAIN_FAILED_TO_OPEN_ICON_LIBRARY_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT));
	}
	else
		ShowRequest(Window,LocaleString(MSG_TERMMAIN_FAILED_TO_OPEN_WORKBENCH_LIBRARY_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT));

	if(!Released)
		ReleaseWindows();

		/* Finished! */

	DoIconify = FALSE;
}

	/* HandleOnlineCleanup():
	 *
	 *	Perform offline cleanup tasks.
	 */

STATIC VOID
HandleOnlineCleanup(BOOL Hangup)
{
	SoundPlay(SOUND_DISCONNECT);

		/* Execute logoff macro. */

	if(Config->CommandConfig->LogoffMacro[0] && WasOnline)
		SerialCommand(Config->CommandConfig->LogoffMacro);

	StopCall(FALSE);

		/* Keep the current connection costs */

	DisplayPay = StopAccountant();

	SetDialMenu(TRUE);

	if(!Hangup)
		Say(LocaleString(MSG_TERMAUX_CARRIER_LOST_TXT));

		/* Clear the password. */

	Password[0] = 0;
	UserName[0]	= 0;

	CurrentBBSName[0]		= 0;
	CurrentBBSComment[0]	= 0;
	CurrentBBSNumber[0]		= 0;

	if(!Config->SerialConfig->CheckCarrier || Config->SerialConfig->DirectConnection)
	{
		ObtainSemaphore(&OnlineSemaphore);
		Online = WasOnline = FALSE;
		ReleaseSemaphore(&OnlineSemaphore);
	}

	SetActivePattern(NULL);
	SetActiveEntry(GlobalPhoneHandle,NULL);

	DisarmLimit();

		/* Previous configuration available? */

	if(BackupConfig)
	{
			/* Remember old configuration. */

		SaveConfig(Config,PrivateConfig);

			/* Copy configuration. */

		SaveConfig(BackupConfig,Config);

			/* Set up new configuration. */

		ConfigSetup();

			/* Free old configuration. */

		DeleteConfiguration(BackupConfig);

		BackupConfig = NULL;
	}

		/* Display the connection cost next time control */
		/* passes through the main loop. */

	ActivateJob(MainJobQueue,DisplayCostJob);

	DisplayHangup = Hangup;

	if(Config->ModemConfig->RedialAfterHangup || KeepRedialing)
	{
		if(GlobalPhoneHandle->DialList)
		{
			if(!IsListEmpty(GlobalPhoneHandle->DialList))
			{
				ObtainSemaphore(&OnlineSemaphore);
				Online = WasOnline = FALSE;
				ReleaseSemaphore(&OnlineSemaphore);

				DoDial = DIAL_LIST;
			}
			else
			{
				if(KeepRedialing)
				{
					KeepRedialing = FALSE;

					if(GlobalPhoneHandle->AutoExit)
						MainTerminated = TRUE;
				}
			}
		}
		else
			KeepRedialing = FALSE;
	}
}

	/* HandleFlowChange():
	 *
	 *	Handle data flow scanner information.
	 */

STATIC VOID
HandleFlowChange()
{
	if(Online)
	{
		if(FlowInfo.NoCarrier)
		{
			if(Config->SerialConfig->CheckCarrier && !Config->SerialConfig->DirectConnection)
			{
					/* Is the carrier still present? */

				if(!(GetSerialStatus() & CIAF_COMCD))
					FlowInfo.NoCarrier = FALSE;
			}

			if(FlowInfo.NoCarrier)
				SetOnlineState(FALSE,0);
		}
	}
	else
	{
		if(FlowInfo.Voice)
		{
			UBYTE DateTimeBuffer[256];

			FormatStamp(NULL,DateTimeBuffer,sizeof(DateTimeBuffer),FALSE);

			WakeUp(Window,SOUND_VOICE);

			ConPrintf(LocaleString(MSG_TERMMAIN_INCOMING_VOIC_CALL_TXT),DateTimeBuffer);

			Say(LocaleString(MSG_TERMMAIN_SAY_INCOMING_VOICE_CALL_TXT));
		}

		if(FlowInfo.Ring)
		{
			UBYTE DateTimeBuffer[256];

			FormatStamp(NULL,DateTimeBuffer,sizeof(DateTimeBuffer),FALSE);

			WakeUp(Window,SOUND_RING);

			ConPrintf(LocaleString(MSG_TERMMAIN_INCOMING_CALL_TXT),DateTimeBuffer);

			Say(LocaleString(MSG_GLOBAL_INCOMING_CALL_TXT));
		}

		if(FlowInfo.Connect)
		{
				/* Are we to check the carrier signal? */

			if(Config->SerialConfig->CheckCarrier && !Config->SerialConfig->DirectConnection)
			{
					/* No carrier signal present? */

				if(GetSerialStatus() & CIAF_COMCD)
					FlowInfo.Connect = FALSE;
			}

			if(FlowInfo.Connect)
			{
				WakeUp(Window,SOUND_CONNECT);

				SetOnlineState(TRUE,0);

				BaudPending = FALSE;

				SetDialMenu(FALSE);
			}
		}
	}

		/* Check if we are to prompt the user for
		 * file transfer.
		 */

	if(FlowInfo.Signature)
	{
		LONG Type = FlowInfo.Signature - SCAN_SIGDEFAULTUPLOAD + TRANSFERSIG_DEFAULTUPLOAD;

		BlockWindows();

		switch(Type)
		{
			case TRANSFERSIG_DEFAULTUPLOAD:

				switch(UploadPanel(TRUE))
				{
					case UPLOAD_TEXT:

#ifdef BUILTIN_ZMODEM
						if(UseInternalZModem)
						{
							InternalZModemTextUpload();
							break;
						}
#endif	/* BUILTIN_ZMODEM */

						StartSendXPR_AskForFile(TRANSFER_TEXT,TRUE);
						break;

					case UPLOAD_BINARY:

#ifdef BUILTIN_ZMODEM
						if(UseInternalZModem)
						{
							InternalZModemBinaryUpload();
							break;
						}
#endif	/* BUILTIN_ZMODEM */

						StartSendXPR_AskForFile(TRANSFER_BINARY,TRUE);
						break;

					case UPLOAD_ABORT:

						CancelZModem();
						break;

					case UPLOAD_BINARY_FROM_LIST:

						HandleUpload(UPLOAD_BINARY);
						break;

					case UPLOAD_TEXT_FROM_LIST:

						HandleUpload(UPLOAD_TEXT);
						break;
				}

				break;

			case TRANSFERSIG_DEFAULTDOWNLOAD:

#ifdef BUILTIN_ZMODEM
				if(UseInternalZModem)
				{
					ZReceive();
					break;
				}
#endif	/* BUILTIN_ZMODEM */

				switch(UploadPanel(FALSE))
				{
					case UPLOAD_TEXT:

						StartReceiveXPR_AskForFile(TRANSFER_TEXT,TRUE);
						break;

					case UPLOAD_BINARY:

						StartReceiveXPR_AskForFile(TRANSFER_BINARY,TRUE);
						break;
				}

				break;

			case TRANSFERSIG_ASCIIUPLOAD:

				if(ChangeProtocol(Config->TransferConfig->ASCIIUploadLibrary,Config->TransferConfig->ASCIIUploadType))
					StartSendXPR_AskForFile(TRANSFER_ASCII,TRUE);

				ResetProtocol();

				break;

			case TRANSFERSIG_ASCIIDOWNLOAD:

				if(ChangeProtocol(Config->TransferConfig->ASCIIDownloadLibrary,Config->TransferConfig->ASCIIDownloadType))
					StartReceiveXPR_AskForFile(TRANSFER_ASCII,TRUE);

				ResetProtocol();

				break;

			case TRANSFERSIG_TEXTUPLOAD:

#ifdef BUILTIN_ZMODEM
				if(UseInternalZModem)
				{
					InternalZModemTextUpload();
					break;
				}
#endif	/* BUILTIN_ZMODEM */

				if(ChangeProtocol(Config->TransferConfig->TextUploadLibrary,Config->TransferConfig->TextUploadType))
					StartSendXPR_AskForFile(TRANSFER_TEXT,TRUE);

				ResetProtocol();

				break;

			case TRANSFERSIG_TEXTDOWNLOAD:

#ifdef BUILTIN_ZMODEM
				if(UseInternalZModem)
				{
					ZReceive();
					break;
				}
#endif	/* BUILTIN_ZMODEM */

				if(ChangeProtocol(Config->TransferConfig->TextDownloadLibrary,Config->TransferConfig->TextDownloadType))
					StartReceiveXPR_AskForFile(TRANSFER_TEXT,TRUE);

				ResetProtocol();

				break;

			case TRANSFERSIG_BINARYUPLOAD:

#ifdef BUILTIN_ZMODEM
				if(UseInternalZModem)
				{
					InternalZModemBinaryUpload();
					break;
				}
#endif	/* BUILTIN_ZMODEM */

				if(ChangeProtocol(Config->TransferConfig->BinaryUploadLibrary,Config->TransferConfig->BinaryUploadType))
					StartSendXPR_AskForFile(TRANSFER_BINARY,TRUE);

				ResetProtocol();

				break;

			case TRANSFERSIG_BINARYDOWNLOAD:

#ifdef BUILTIN_ZMODEM
				if(UseInternalZModem)
				{
					ZReceive();
					break;
				}
#endif	/* BUILTIN_ZMODEM */

				if(ChangeProtocol(Config->TransferConfig->BinaryDownloadLibrary,Config->TransferConfig->BinaryDownloadType))
					StartReceiveXPR_AskForFile(TRANSFER_BINARY,TRUE);

				ResetProtocol();

				break;
		}

		ReleaseWindows();
	}

	ResetDataFlowFilter();
}

	/* FullHangup():
	 *
	 *	In a nutshell, do the full work required to hang up the line.
	 */

STATIC VOID
FullHangup(BOOL ForceIt)
{
	BlockWindows();

	DispatchRexxDialMsgList(FALSE);

	HangUp();

	ReleaseWindows();

	if(Config->SerialConfig->CheckCarrier && !ForceIt && !Config->SerialConfig->DirectConnection)
		HungUp = TRUE;
	else
	{
		SetOnlineState(FALSE,0);

		HandleOnlineCleanup(TRUE);
	}
}

	/* AskDial(struct Window *Parent):
	 *
	 *	This is called when the user is about to start dialing. If the
	 *	dial button/menu item is available with the protective mode
	 *	enabled, a message will be displayed, asking if the line
	 *	should be hung up before proceeding.
	 */

STATIC BOOL
AskDial(struct Window *Parent)
{
	if(!DialItemsAvailable)
	{
		if(ShowRequest(Parent,LocaleString(MSG_CANNOT_DIAL_BECAUSE_TXT),LocaleString(MSG_DIAL_CANCEL_TXT)))
			FullHangup(TRUE);
		else
			return(FALSE);
	}

	return(TRUE);
}

	/* GoOnline():
	 *
	 *	Go into online state.
	 */

STATIC VOID
GoOnline()
{
	SetOnlineState(TRUE,0);

	BaudCount		= 0;
	BaudBuffer[0]	= 0;
	BaudPending		= FALSE;

	Password[0]		= 0;
	UserName[0]		= 0;

	SendStartup		= FALSE;

	DisarmLimit();

	CurrentBBSName[0]		= 0;
	CurrentBBSComment[0]	= 0;
	CurrentBBSNumber[0]		= 0;

	SetActivePattern(NULL);
	SetActiveEntry(GlobalPhoneHandle,NULL);

	SetDialMenu(FALSE);
}

STATIC VOID
ReadyToDial(struct DataDialMsg *DialMsg)
{
	struct GenericDialNode *Node;
	PhoneEntry **Phonebook;
	BOOL DidSomething;

	DidSomething = FALSE;

	AddTail(&RexxDialMsgList,(struct Node *)DialMsg->DialMsg);

	Phonebook = GlobalPhoneHandle->Phonebook;

	while(Node = (struct GenericDialNode *)RemHead(DialMsg->DialList))
	{
		if(Node->Index != -1)
		{
			if(Node->Index >= 0 && Node->Index < GlobalPhoneHandle->NumPhoneEntries)
				DidSomething |= AddDialEntry(GlobalPhoneHandle,Phonebook[Node->Index],NULL);
		}
		else
		{
			if(Node->Number != NULL)
				DidSomething |= AddDialEntry(GlobalPhoneHandle,NULL,Node->Number);
			else
			{
				LONG i;

				for(i = 0 ; i < GlobalPhoneHandle->NumPhoneEntries ; i++)
				{
					if(!Stricmp(Phonebook[i]->Header->Name,Node->Node.ln_Name))
					{
						if(Phonebook[i]->Header->Number[0])
							DidSomething |= AddDialEntry(GlobalPhoneHandle,Phonebook[i],NULL);
					}
				}
			}
		}

		FreeVecPooled(Node);
	}

	if(DidSomething)
		DoDial = DIAL_LIST;
}

STATIC VOID
HandleUpload(UBYTE Type)
{
	LockGenericList(GenericListTable[GLIST_UPLOAD]);

	if(GenericListCount(GenericListTable[GLIST_UPLOAD]) > 0)
	{
		struct FileTransferInfo *Info;

		if(!(Info = AllocFileTransferInfo()))
			UnlockGenericList(GenericListTable[GLIST_UPLOAD]);
		else
		{
			LONG FilesFound;
			APTR OldPtr;
			struct Node *Node;
			BPTR FileLock;
			D_S(struct FileInfoBlock,FileInfo);

			FilesFound = 0;

			DisableDOSRequesters(&OldPtr);

			for(Node = ((struct List *)GenericListTable[GLIST_UPLOAD])->lh_Head ; Node->ln_Succ ; Node = Node->ln_Succ)
			{
				if(FileLock = Lock(Node->ln_Name,ACCESS_READ))
				{
					if(Examine(FileLock,FileInfo))
					{
						if(FileInfo->fib_DirEntryType < 0)
						{
							if(AddFileTransferNode(Info,Node->ln_Name,FileInfo->fib_Size))
								FilesFound++;

							if(Config->TransferConfig->TransferIcons)
							{
								UBYTE LocalBuffer[MAX_FILENAME_LENGTH];
								BPTR InfoLock;

								LimitedSPrintf(sizeof(LocalBuffer),LocalBuffer,"%s.info",Node->ln_Name);

								if(InfoLock = Lock(LocalBuffer,ACCESS_READ))
								{
									if(Examine(InfoLock,FileInfo))
									{
										if(FileInfo->fib_DirEntryType < 0)
										{
											if(AddFileTransferNode(Info,LocalBuffer,FileInfo->fib_Size))
												FilesFound++;
										}
									}

									UnLock(InfoLock);
								}
							}
						}
					}

					UnLock(FileLock);
				}
			}

			ClearGenericList(GenericListTable[GLIST_UPLOAD],TRUE);

			UnlockGenericList(GenericListTable[GLIST_UPLOAD]);

			EnableDOSRequesters(OldPtr);

			if(FilesFound)
			{
				BlockWindows();

				ReadyFileTransferInfo(Info,FALSE);

				FileTransferInfo = Info;

				StartSendXPR_FromList(Type == UPLOAD_BINARY ? TRANSFER_BINARY : TRANSFER_TEXT,TRUE);

				ReleaseWindows();
			}
			else
				FreeFileTransferInfo(Info);
		}
	}
	else
	{
		UnlockGenericList(GenericListTable[GLIST_UPLOAD]);

		StartSendXPR_AskForFile(Type == UPLOAD_BINARY ? TRANSFER_BINARY : TRANSFER_TEXT,TRUE);
	}
}

	/* LocalReleaseSerial():
	 *
	 *	Release the serial device driver, then reopen it again.
	 */

STATIC VOID
LocalReleaseSerial()
{
	BOOL Continue,SerialClosed;
	APTR OldPtr;

	DB(kprintf("in: LocalReleaseSerial()\n"));

	ChangeWindowPtr(&OldPtr,(APTR)Window);

		/* This might happen if an ARexx user
		 * released the serial device and
		 * failed to reopen it.
		 */

	if(ReadPort)
		SerialClosed = FALSE;
	else
		SerialClosed = TRUE;

	DB(kprintf("blockwindows\n"));

	BlockWindows();

		/* Prevent catastrophes! */

	if(!Config->MiscConfig->ProtectiveMode)
		Continue = TRUE;
	else
	{
		if(Online && !SerialClosed)
		{
			if(!ShowRequest(Window,LocaleString(MSG_TERMMAIN_YOU_ARE_STILL_ONLINE_TXT),LocaleString(MSG_GLOBAL_YES_NO_TXT)))
				Continue = FALSE;
			else
				Continue = TRUE;
		}
		else
			Continue = TRUE;
	}

	DB(kprintf("continue=%ld serialclosed=%ld\n",Continue,SerialClosed));

	if(Continue)
	{
		if(SerialClosed)
		{
			DB(kprintf("reopenserial\n"));

			ReopenSerial();
		}
		else
		{
			DB(kprintf("clearserial\n"));
			ClearSerial();

			DB(kprintf("deleteserial\n"));
			DeleteSerial();

			switch(ShowRequest(Window,LocaleString(MSG_TERMMAIN_UNIT_RESET_AND_RELEASED_TXT),LocaleString(MSG_TERMMAIN_RETURN_QUIT_TXT),Config->SerialConfig->SerialDevice,Config->SerialConfig->UnitNumber))
			{
				case 0:

					MainTerminated = TRUE;
					break;

				case 1:

					ReopenSerial();
					break;

				case 2:

					ActivateJob(MainJobQueue,IconifyJob);
					break;
			}
		}
	}

	RestoreWindowPtr(OldPtr);

	ReleaseWindows();

	DB(kprintf("out: LocalReleaseSerial()\n"));
}

	/* HandleKeyboardInput():
	 *
	 *	Process the keyboard input.
	 */

STATIC VOID
HandleKeyboardInput(UBYTE Char,UWORD Code,ULONG Qualifier,STRPTR InputBuffer,LONG Len)
{
		/* Take care of the numeric keypad. */

	if((Qualifier & IEQUALIFIER_NUMERICPAD) && (Config->EmulationConfig->NumericMode == KEYMODE_APPLICATION))
	{
		STATIC STRPTR StringTable[22][2] =
		{
			"0",	"\033Op",
			"1",	"\033Oq",
			"2",	"\033Or",
			"3",	"\033Os",
			"4",	"\033Ot",
			"5",	"\033Ou",
			"6",	"\033Ov",
			"7",	"\033Ow",
			"8",	"\033Ox",
			"9",	"\033Oy",
			"-",	"\033Om",
			"+",	"\033Ol",	/* This should really be a comma */
			".",	"\033On",

			"(",	"\033OP",
			"[",	"\033OP",
			"{",	"\033OP",
			"]",	"\033OQ",
			")",	"\033OQ",
			"}",	"\033OQ",
			"/",	"\033OR",
			"*",	"\033OS",

			"\r",	"\033OM"
		};

		STATIC struct { UBYTE Code; STRPTR String; } CodeTable[18] =
		{
			0x0F,	"\033Op",	/* "0" */
			0x1D,	"\033Oq",	/* "1" */
			0x1E,	"\033Or",	/* "2" */
			0x1F,	"\033Os",	/* "3" */
			0x2D,	"\033Ot",	/* "4" */
			0x2E,	"\033Ou",	/* "5" */
			0x2F,	"\033Ov",	/* "6" */
			0x3D,	"\033Ow",	/* "7" */
			0x3E,	"\033Ox",	/* "8" */
			0x3F,	"\033Oy",	/* "9" */
			0x4A,	"\033Om",	/* "-" */
			0x5E,	"\033Ol",	/* "+", but this should really be a comma */
			0x3C,	"\033On",	/* "." */

			0x5A,	"\033OP",	/* "[" */
			0x5B,	"\033OQ",	/* "]" */
			0x5C,	"\033OR",	/* "/" */
			0x5D,	"\033OS",	/* "*" */

			0x43,	"\033OM"	/* <cr> */
		};

		STRPTR	String = NULL;
		LONG	i;

		for(i = 0 ; i < 22 ; i++)
		{
			if(Char == StringTable[i][0][0])
			{
				String = StringTable[i][1];

				break;
			}
		}

		if(!String)
		{
			for(i = 0 ; i < 18 ; i++)
			{
				if(Code == CodeTable[i].Code)
				{
					String = CodeTable[i].String;

					break;
				}
			}
		}

		if(String)
		{
			if(ClipInput)
			{
				CloseClip();

				ClipInput = ClipXerox = ClipPrefix = FALSE;
			}

			SerWrite(String,strlen(String));

			Len = 0;
		}
	}

		/* This looks like a raw, or better, now cooked key. */

	if(Len)
	{
		switch(CharType[Char])
		{
			case CHAR_HELP:

				GuideDisplay(CONTEXT_MAIN);

				Len = 0;

				break;

			case CHAR_CURSOR:

				if(ClipInput)
				{
					CloseClip();

					ClipInput = ClipXerox = ClipPrefix = FALSE;
				}

					/* If in cursor key applications mode,
					 * send the corresponding string.
					 */

				if(Config->EmulationConfig->CursorMode == KEYMODE_APPLICATION)
				{
					STATIC STRPTR CursorTable[4] =
					{
						"\033OA",
						"\033OB",
						"\033OC",
						"\033OD"
					};

					SerWrite(CursorTable[Char - CUP],3);
				}
				else
				{
					LONG QualType;

						/* Find the appropriate qualifier. */

					if(Qualifier & SHIFT_KEY)
						QualType = 1;
					else
					{
						if(Qualifier & ALT_KEY)
							QualType = 2;
						else
						{
							if(Qualifier & CONTROL_KEY)
								QualType = 3;
							else
								QualType = 0;
						}
					}

						/* Send the corresponding string. */

					SerialCommand(CursorKeys->Keys[QualType][Char - CUP]);
				}

				Len = 0;

				break;

				/* Any function key pressed? */

			case CHAR_FUNCTION:

				if(ClipInput)
				{
					CloseClip();

					ClipInput = ClipXerox = ClipPrefix = FALSE;
				}

				if(Qualifier & CONTROL_KEY)
					SerialCommand(MacroKeys->Keys[3][Char - FN1]);
				else
				{
					if(Qualifier & ALT_KEY)
						SerialCommand(MacroKeys->Keys[2][Char - FN1]);
					else
					{
						if(Qualifier & SHIFT_KEY)
							SerialCommand(MacroKeys->Keys[1][Char - FN1]);
						else
							SerialCommand(MacroKeys->Keys[0][Char - FN1]);
					}
				}

				Len = 0;

				break;

				/* Anything else? */

			default:

				if(Len == 1 && Char == '\r' && Recording && !RecordingLine && (Qualifier & SHIFT_KEY))
				{
					RememberInputText("\r",1);

					Len = 0;

					PopStatus();
					PushStatus(STATUS_RECORDING_LINE);

					RecordingLine = TRUE;

					RememberResetInput();

					RememberOutput = FALSE;
					RememberInput = TRUE;

					CheckItem(MEN_RECORD_LINE,TRUE);
				}

				break;
		}

			/* Any characters to send? */

		if(Len)
			SendInputTextBuffer(InputBuffer,Len,TRUE,FALSE);
	}
}

	/* HandleSerialCheckJob():
	 *
	 *	Handle routine checkup actions.
	 */

BOOL
HandleSerialCheckJob(JobNode *UnusedJob)
{
		/* Attempt to lock the serial device? */

	if(PollODU)
	{
			/* We don't want to poll too often */

		if(PollODUCount++ >= (5 + ROUTINE_CHECK_INTERVAL - 1) / ROUTINE_CHECK_INTERVAL)
		{
			PollODUCount = 0;

				/* Still supporting the locking protocol? */

			if(Config->SerialConfig->UseOwnDevUnit)
			{
				BlockWindows();

					/* Give it a try */

				if(LockDevice(Config->SerialConfig->SerialDevice,Config->SerialConfig->UnitNumber,NULL,0))
				{
						/* Check */

					ReopenSerial();

					PollODU = FALSE;
				}

				ReleaseWindows();
			}
			else
				PollODU = FALSE;
		}
	}

		/* Take a look at the carrier signal. */

	if(Config->SerialConfig->CheckCarrier && WriteRequest && !Config->SerialConfig->DirectConnection)
	{
		UWORD Status = GetSerialStatus();

			/* Still online? */

		if(Online)
		{
				/* Carrier detect signal lost? */

			if(Status & CIAF_COMCD)
				SetOnlineState(FALSE,0);
		}
		else
		{
				/* Is the carrier detect signal
				 * present?
				 */

			if(!(Status & CIAF_COMCD))
				GoOnline();
		}
	}

		/* Check online time limit. */

	if(CheckLimit())
		LaunchRexxAsync(LimitMacro);

		/* Flush capture file contents to disk,
		 * this routine is executed each minute
		 * in order to store the captured data.
		 */

	if(BufferFlushCount++ >= (60 + ROUTINE_CHECK_INTERVAL - 1) / ROUTINE_CHECK_INTERVAL)
	{
		BufferFlushCount = 0;

			/* Flush the capture file. */

		if(FileCapture)
			BufferFlush(FileCapture);
	}

	return(FALSE);
}

	/* HandleQueueJob(JobNode *UnusedJob):
	 *
	 *	Process the special message queue.
	 */

BOOL
HandleQueueJob(JobNode *UnusedJob)
{
	struct DataMsg *Item;

	if(Item = GetMsgItem(SpecialQueue))
	{
		WORD OldEcho,Type;

		switch(Item->Type)
		{
				/* Output data. */

			case DATAMSGTYPE_WRITE:

				SerWrite(Item->Data,Item->Size);
				break;

				/* Execute a command. */

			case DATAMSGTYPE_SERIALCOMMAND:

				SerialCommand(Item->Data);
				break;

				/* Execute a command, but don't echo it. */

			case DATAMSGTYPE_SERIALCOMMANDNOECHO:

				OldEcho = Config->SerialConfig->Duplex;

				Config->SerialConfig->Duplex = DUPLEX_FULL;

				SerialCommand(Item->Data);

				Config->SerialConfig->Duplex = OldEcho;
				break;

				/* Output contents of clipboard */

			case DATAMSGTYPE_WRITECLIP:

				if(!ClipInput)
				{
					if(!OpenClip(Item->Size))
						ClipInput = ClipXerox = TRUE;
					else
						ClipInput = ClipXerox = FALSE;
				}

					/* Are we reading input from the clipboard? */

				if(ClipInput)
				{
					UBYTE InputBuffer[256];
					LONG Len;

					if((Len = GetClip(InputBuffer,sizeof(InputBuffer) - 1)) < 0)
					{
						CloseClip();

						ClipInput = FALSE;

						if(ClipXerox)
						{
							if(Config->ClipConfig->InsertSuffix[0])
								SerialCommand(Config->ClipConfig->InsertSuffix);

							ClipXerox = FALSE;
						}

						ClipPrefix = FALSE;
					}
					else
					{
						if(!ClipPrefix && ClipXerox)
						{
							if(Config->ClipConfig->InsertPrefix[0])
								SerialCommand(Config->ClipConfig->InsertPrefix);

							ClipPrefix = TRUE;
						}

						if(Len > 0)
							SendInputTextBuffer(InputBuffer,Len,FALSE,Config->ClipConfig->ConvertLF);
					}
				}

				break;

				/* Start an upload */

			case DATAMSGTYPE_UPLOAD:

				Type = Item->Size;

				DeleteMsgItem((struct MsgItem *)Item);
				Item = NULL;

				HandleUpload(Type);
				break;

				/* Call a menu item */

			case DATAMSGTYPE_MENU:

				HandleMenuCode(NULL,(ULONG)Item->Size,(ULONG)Item->Data);
				break;

				/* Rendezvous with external process */

			case DATAMSGTYPE_RENDEZVOUS:

				if(ReadRequest && WriteRequest)
				{
						/* Abort serial I/O processing */

					ClearSerial();

					BlockWindows();

						/* Return the message, caution, we're not ready yet */

					Forbid();

					DeleteMsgItem((struct MsgItem *)Item);

					Item = NULL;

						/* Prepare to wait... */

					WaitForHandshake();

						/* Pick up the queue */

					RestartSerial();

					Permit();

					ReleaseWindows();
				}

				break;

				/* Discard current buffer contents */

			case DATAMSGTYPE_CLEARBUFFER:

				FreeBuffer();
				break;

				/* Redial current dial list */

			case DATAMSGTYPE_REDIAL:

				if(!Online)
					DoDial = DIAL_LIST;

				break;

				/* Hang up the line */

			case DATAMSGTYPE_HANGUP:

				FullHangup(FALSE);
				break;

				/* Go to online state */

			case DATAMSGTYPE_GOONLINE:

				if(!Online)
					GoOnline();

				break;

				/* Dial a list of numbers */

			case DATAMSGTYPE_DIAL:

				ReadyToDial((struct DataDialMsg *)Item);
				break;

				/* Output data as if it had come from the main window. */

			case DATAMSGTYPE_WRITESTRING:

				SendInputTextBuffer(Item->Data,Item->Size,TRUE,TRUE);
				break;

				/* Output data as if it had come from the main window. */

			case DATAMSGTYPE_WRITESTRING_NOECHO:

				OldEcho = Config->SerialConfig->Duplex;

				Config->SerialConfig->Duplex = DUPLEX_FULL;

				SendInputTextBuffer(Item->Data,Item->Size,TRUE,TRUE);

				Config->SerialConfig->Duplex = OldEcho;
				break;
		}

		if(Item)
			DeleteMsgItem((struct MsgItem *)Item);

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

	/* HandleOwnDevUnitJob():
	 *
	 *	Deal with the OwnDevUnit signal notification.
	 */

BOOL
HandleOwnDevUnitJob(JobNode *UnusedJob)
{
	DB(kprintf("handle ODU\n"));

	if(!Online || !ReadPort || (Online && Config->SerialConfig->ReleaseODUWhenOnline))
	{
		if(Config->SerialConfig->SatisfyODURequests == ODU_RELEASE)
		{
			DB(kprintf("ODU release\n"));
			LocalReleaseSerial();
		}

		if(Config->SerialConfig->SatisfyODURequests == ODU_WAIT)
		{
			BOOL Continue,SerialClosed;
			APTR OldPtr;

			DB(kprintf("ODU wait\n"));

			ChangeWindowPtr(&OldPtr,(APTR)Window);

				/* This might happen if an ARexx user
				 * released the serial device and
				 * failed to reopen it.
				 */

			if(ReadPort)
				SerialClosed = FALSE;
			else
				SerialClosed = TRUE;

			BlockWindows();

				/* Prevent catastrophes! */

			if(Online && !SerialClosed && !Config->SerialConfig->ReleaseODUWhenOnline)
			{
				if(!ShowRequest(Window,LocaleString(MSG_TERMMAIN_YOU_ARE_STILL_ONLINE_TXT),LocaleString(MSG_GLOBAL_YES_NO_TXT)))
					Continue = FALSE;
				else
					Continue = TRUE;
			}
			else
				Continue = TRUE;

			if(Continue)
			{
				if(!SerialClosed)
				{
					ClearSerial();

					DeleteSerial();
				}
			}

			RestoreWindowPtr(OldPtr);

			ReleaseWindows();

				/* Start polling for the device to become */
				/* available again */

			PollODU = TRUE;
		}
	}

	DB(kprintf("ODU done\n"));

	return(FALSE);
}

	/* HandleWindowJob(JobNode *UnusedJob):
	 *
	 *	Just makes a call to HandleWindow().
	 */

BOOL
HandleWindowJob(JobNode *UnusedJob)
{
	return(HandleWindow());
}

	/* HandleWindowAndXEMJob(JobNode *UnusedJob):
	 *
	 *	Calls both HandleXEM() and HandleWindow().
	 */

BOOL
HandleWindowAndXEMJob(JobNode *UnusedJob)
{
	HandleXEM();

	return(HandleWindow());
}

	/* HandleWindow():
	 *
	 *	This funny part checks the window(s) for incoming
	 *	user input. Menus are handled elsewhere.
	 */

BOOL
HandleWindow()
{
	STATIC ULONG	ClickSeconds,ClickMicros;
	STATIC ULONG	LastSeconds,LastMicros;
	STATIC BOOL		TestingDoubleClick;

	struct IntuiMessage	*Message;
	ULONG				 MsgClass,Code,Qualifier,Seconds,Micros;
	LONG				 MouseX,MouseY,Len,GadgetID;
	struct Gadget		*Gadget;
	UBYTE				 Char,InputBuffer[256];
	struct Window		*IDCMPWindow;
	BOOL				 Result,WindowJustActivated;

	Result				= FALSE;
	WindowJustActivated	= FALSE;

		/* Are we reading input from the clipboard? */

	if(ClipInput)
	{
		LONG Length = GetClip(InputBuffer,sizeof(InputBuffer) - 1);

		if(Length < 0)
		{
			CloseClip();

			ClipInput = FALSE;

			if(ClipXerox)
			{
				if(Config->ClipConfig->InsertSuffix[0])
					SerialCommand(Config->ClipConfig->InsertSuffix);

				ClipXerox = FALSE;
			}

			ClipPrefix = FALSE;
		}
		else
		{
			if(!ClipPrefix && ClipXerox)
			{
				if(Config->ClipConfig->InsertPrefix[0])
					SerialCommand(Config->ClipConfig->InsertPrefix);

				ClipPrefix = TRUE;
			}

			if(Length > 0)
				SendInputTextBuffer(InputBuffer,Length,FALSE,Config->ClipConfig->ConvertLF);

			Result = TRUE;
		}
	}

		/* Any news in the mail? */

	if(Message = (struct IntuiMessage *)GetMsg(Window->UserPort))
	{
			/* A click into the window should activate it, but
			 * we don't want to have the character snapping activated
			 * under these conditions. In this case we rely upon
			 * Intuition sending the IDCMP_ACTIVEWINDOW and
			 * IDCMP_MOUSEBUTTONS event marked with the same
			 * creation time stamp. Even if the Intuition
			 * implementation should change no harm should
			 * be done.
			 */

		Seconds	= Message->Seconds;
		Micros	= Message->Micros;

		if(Seconds == LastSeconds && Micros == LastMicros && Message->IDCMPWindow == Window)
		{
			if(Message->Class == IDCMP_ACTIVEWINDOW || Message->Class == IDCMP_MOUSEBUTTONS)
				WindowJustActivated = TRUE;
		}

		LastSeconds	= Seconds;
		LastMicros	= Micros;

			/* Pick up the pieces. */

		MsgClass	= Message->Class;
		Code		= Message->Code;
		Qualifier	= Message->Qualifier;
		Gadget		= (struct Gadget *)Message->IAddress;

		MouseX		= Message->MouseX;
		MouseY		= Message->MouseY;

		IDCMPWindow	= Message->IDCMPWindow;

		Len		= 0;
		Char	= 0;

		switch(MsgClass)
		{
			case IDCMP_IDCMPUPDATE:

				GadgetID = GetTagData(GA_ID,0,(struct TagItem *)Gadget);
				break;

			case IDCMP_GADGETUP:
			case IDCMP_GADGETDOWN:

				GadgetID = Gadget->GadgetID;
				break;

			case IDCMP_RAWKEY:

					/* Perform key conversion. */

				if(XEmulatorBase)
				{
					if(Len = XEmulatorUserMon(XEM_IO,InputBuffer,sizeof(InputBuffer) - 1,Message))
						Char = InputBuffer[0];
				}
				else
					Char = KeyConvert(Message,InputBuffer,&Len);

				/* Falls through to... */

			default:

				GadgetID = 0;
				break;
		}

		ReplyMsg((struct Message *)Message);

			/* The following messages originated from
			 * the fast! macro panel.
			 */

		if(IDCMPWindow == FastWindow)
		{
			switch(MsgClass)
			{
					/* Close the window. */

				case IDCMP_CLOSEWINDOW:

					CloseFastWindow();
					return(TRUE);

					/* Gadget or window size change event. */

				case IDCMP_GADGETUP:
				case IDCMP_NEWSIZE:

					HandleFastWindowGadget(MsgClass,Code);
					return(TRUE);
			}
		}

			/* Status window activated? */

		if(IDCMPWindow == StatusWindow)
		{
			switch(MsgClass)
			{
				case IDCMP_ACTIVEWINDOW:

					if(!Config->ScreenConfig->SplitStatus)
						NormalCursor();

					break;

				case IDCMP_CLOSEWINDOW:

					ShakeHands((struct Task *)StatusProcess,SIG_CLOSEWINDOW);

					ClearMenuStrip(StatusWindow);
					CloseWindowSafely(StatusWindow);

					StatusWindow = NULL;
					break;
			}
		}

			/* What about the matrix window? */

		if(IDCMPWindow == MatrixWindow)
		{
			if(DispatchMatrixWindow(&MsgClass,Code,Qualifier,Char))
				CloseMatrixWindow();
		}

			/* Main window message? */

		if(IDCMPWindow == Window)
		{
			switch(MsgClass)
			{
				case IDCMP_GADGETUP:

					if(GadgetID == CHAT_GadgetID)
						HandleChatGadget(Code);

					break;

				case IDCMP_GADGETDOWN:

					if(GadgetID == CHAT_GadgetID)
						MarkChatGadgetAsActive();

					break;

				case IDCMP_INACTIVEWINDOW:

					WindowMarkerInterrupt();

					GhostCursor();

					break;

				case IDCMP_ACTIVEWINDOW:

						/* Take care of the chat gadget if necessary */

					ActivateChat(TRUE);

					NormalCursor();

					break;

				case IDCMP_NEWSIZE:

						/* Is a window clipping region installed? */

					if(ClipRegion)
					{
						struct Rectangle RegionRectangle;

							/* Install old region. */

						InstallClipRegion(Window->WLayer,OldRegion);

							/* Fill in the clipping rectangle. */

						RegionRectangle.MinX = Window->BorderLeft;
						RegionRectangle.MinY = Window->BorderTop;
						RegionRectangle.MaxX = Window->Width - (Window->BorderRight + 1);
						RegionRectangle.MaxY = Window->Height - (Window->BorderBottom + 1);

							/* Clear previous clipping region. */

						ClearRegion(ClipRegion);

							/* Set new clipping region. */

						OrRectRegion(ClipRegion,&RegionRectangle);

							/* Install new clipping region. */

						OldRegion = InstallClipRegion(Window->WLayer,ClipRegion);
					}

					ForceStatusUpdate();

					HandleMenuCode(NULL,MEN_RESET_TERMINAL,Qualifier);

						/* Take care of the chat gadget if necessary */

					ActivateChat(TRUE);

					TTYResize();

					break;

				case IDCMP_CLOSEWINDOW:

					HandleMenuCode(NULL,MEN_QUIT,Qualifier);
					break;

				case IDCMP_MOUSEMOVE:

					WindowMarkerMoveMouse();
					break;

				case IDCMP_MOUSEBUTTONS:

					if(Code == SELECTUP)
						ActivateChat(TRUE);

					if((!WindowJustActivated || Code != SELECTDOWN) && (!XEmulatorBase || Config->TerminalConfig->EmulationMode != EMULATION_EXTERNAL))
					{
						switch(Code)
						{
							case SELECTUP:

								WindowMarkerInterrupt();
								break;

							case SELECTDOWN:

								MouseX -= WindowLeft;

								if(MouseX < 0)
									MouseX = 0;

								if(MouseX > WindowWidth - 1)
									MouseX = WindowWidth - 1;

								MouseY -= WindowTop;

								if(MouseY < 0)
									MouseY = 0;

								if(MouseY > WindowHeight - 1)
									MouseY = WindowHeight - 1;

								if(Qualifier & CONTROL_KEY)
								{
									LONG FirstX,FirstY;

									FirstX = (MouseX * CharCellDenominator) / (TextFontWidth * CharCellNominator);
									FirstY = MouseY / TextFontHeight;

									if(FirstX < RasterWidth && FirstY < RasterHeight)
									{
										UBYTE LocalChar[2];

										SafeObtainSemaphoreShared(&RasterSemaphore);

										LocalChar[0] = Raster[FirstY * RasterWidth + FirstX];

										ReleaseSemaphore(&RasterSemaphore);

										if(LocalChar[0])
										{
											LONG Len;

											if(Qualifier & SHIFT_KEY)
											{
												LocalChar[1] = '\r';
												Len = 2;
											}
											else
												Len = 1;

											SendInputTextBuffer(LocalChar,Len,TRUE,TRUE);
										}
									}

									return(TRUE);
								}

								if((Qualifier & ALT_KEY) && (Qualifier & IEQUALIFIER_LEFTBUTTON))
								{
									LONG DeltaX,DeltaY;

									ObtainSemaphore(&TerminalSemaphore);

									DeltaX = (MouseX * CharCellDenominator) / (TextFontWidth * CharCellNominator)  - CursorX;
									DeltaY = MouseY / TextFontHeight - CursorY;

									ReleaseSemaphore(&TerminalSemaphore);

									if(DeltaX || DeltaY)
									{
										if(DeltaX > 0)
										{
											DeltaX++;

											while(DeltaX--)
												SerWrite("\33[C",3);
										}

										if(DeltaX < 0)
										{
											while(DeltaX++)
												SerWrite("\33[D",3);
										}

										if(DeltaY > 0)
										{
											DeltaY++;

											while(DeltaY--)
												SerWrite("\33[B",3);
										}

										if(DeltaY < 0)
										{
											while(DeltaY++)
												SerWrite("\33[A",3);
										}
									}

									return(TRUE);
								}

								if(TestingDoubleClick)
								{
									ULONG CurrentSeconds,CurrentMicros;

									TestingDoubleClick = FALSE;

									CurrentTime(&CurrentSeconds,&CurrentMicros);

									if(DoubleClick(ClickSeconds,ClickMicros,CurrentSeconds,CurrentMicros))
									{
										WindowMarkWord();

										return(TRUE);
									}
								}
								else
								{
									TestingDoubleClick = TRUE;

									CurrentTime(&ClickSeconds,&ClickMicros);
								}

								WindowMarkerStart(Qualifier);
								break;
						}
					}

					break;
			}
		}

			/* Now for general information. */

		switch(MsgClass)
		{
			case IDCMP_REFRESHWINDOW:

				BeginRefresh(IDCMPWindow);
				EndRefresh(IDCMPWindow,TRUE);
				break;

			case IDCMP_RAWKEY:

				HandleKeyboardInput(Char,Code,Qualifier,InputBuffer,Len);
				break;

				/* A menu item was selected. */

			case IDCMP_MENUPICK:

				HandleMenu(Code,Qualifier);

					/* Take care of the chat gadget if necessary */

				ActivateChat(TRUE);

				break;

				/* Menu help is required. */

			case IDCMP_MENUHELP:

				if(MENUNUM(Code) == NOMENU || MENUNUM(Code) > 9 || ITEMNUM(Code) == NOITEM)
					GuideDisplay(CONTEXT_MAIN_MENU);
				else
					GuideDisplay(CONTEXT_PROJECT_MEN + MENUNUM(Code));

				break;
		}

		return(TRUE);
	}

	return(Result);
}

	/* HandleMenuCode(ULONG Code,ULONG Qualifier):
	 *
	 *	Handle each function associated with a menu code.
	 */

VOID
HandleMenuCode(struct MenuItem *ThisItem,ULONG Code,ULONG Qualifier)
{
	struct FileRequester	*FileRequest;
	UBYTE					 DummyBuffer[MAX_FILENAME_LENGTH];

	struct Window			*ReqWindow;
	struct EasyStruct		 Easy;

	BPTR					 SomeFile;
	APTR					 OldPtr;

	BOOL					 Checked;

	switch(Code)
	{
			/* Save screen as IFF-ILBM file. */

		case MEN_SAVE_AS_PICTURE:

			BlockWindows();

			DummyBuffer[0] = 0;

			if(FileRequest = SaveFile(Window,LocaleString(MSG_TERMMAIN_SAVE_SCREEN_IFF_TXT),NULL,NULL,DummyBuffer,sizeof(DummyBuffer)))
			{
				FreeAslRequest(FileRequest);

				if(!SaveWindow(DummyBuffer,Window))
					ShowError(Window,ERR_SAVE_ERROR,IoErr(),DummyBuffer);
			}

			ReleaseWindows();

			break;

			/* Save screen as ASCII file. */

		case MEN_SAVE_AS_TEXT:

			BlockWindows();

			DummyBuffer[0] = 0;

			if(FileRequest = SaveFileOverwrite(Window,LocaleString(MSG_TERMMAIN_SAVE_SCREEN_ASCII_TXT),NULL,NULL,DummyBuffer,sizeof(DummyBuffer)))
			{
				LONG Error = 0;

				FreeAslRequest(FileRequest);

				SomeFile = NULL;

				if(GetFileSize(DummyBuffer))
				{
					switch(ShowRequest(Window,LocaleString(MSG_GLOBAL_FILE_ALREADY_EXISTS_TXT),LocaleString(MSG_GLOBAL_CREATE_APPEND_CANCEL_TXT),DummyBuffer))
					{
						case 0:

							SetIoErr(0);
							break;

						case 1:

							SomeFile = Open(DummyBuffer,MODE_NEWFILE);
							break;

						case 2:

							SomeFile = OpenToAppend(DummyBuffer,NULL);
							break;
					}
				}
				else
					SomeFile = Open(DummyBuffer,MODE_NEWFILE);

				if(SomeFile)
				{
					UBYTE *Buffer;
					LONG i,j;

					for(i = 0 ; i < RasterHeight ; i++)
					{
						Buffer = &Raster[i * RasterWidth];

						j = RasterWidth - 1;

						while(j >= 0 && Buffer[j] == ' ')
							j--;

						if(j >= 0)
						{
							SetIoErr(0);

							if(FWrite(SomeFile,Buffer,j + 1,1) < 1)
							{
								Error = IoErr();

								break;
							}
						}

						SetIoErr(0);

						if(FWrite(SomeFile,"\n",1,1) < 1)
						{
							Error = IoErr();

							break;
						}
					}

					Close(SomeFile);

					AddProtection(DummyBuffer,FIBF_EXECUTE);

					if(Config->MiscConfig->CreateIcons)
						AddIcon(DummyBuffer,FILETYPE_TEXT,TRUE);
				}
				else
					Error = IoErr();

				if(Error)
					ShowError(Window,ERR_SAVE_ERROR,Error,DummyBuffer);
			}

			ReleaseWindows();

			break;

			/* Print the screen (pure ASCII). */

		case MEN_PRINT_SCREEN:

			BlockWindows();

			if(RasterEnabled)
				PrintSomething(PRINT_SCREEN);
			else
				ShowRequest(Window,LocaleString(MSG_TERMMAIN_NO_DATA_TO_PRINT_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT));

			ReleaseWindows();

			break;

			/* Print the screen (graphics). */

		case MEN_PRINT_SCREEN_AS_GFX:

			BlockWindows();

			PrintScreenGfx();

			ReleaseWindows();

			break;

			/* Print the clipboard contents. */

		case MEN_PRINT_CLIP:

			BlockWindows();

			PrintSomething(PRINT_CLIP);

			ReleaseWindows();

			break;

			/* Open/close the terminal capture file. */

		case MEN_CAPTURE_TO_FILE:

			if(FileCapture)
				CloseFileCapture();
			else
				OpenFileCapture(FALSE);

			break;

			/* Open/close the terminal capture file. */

		case MEN_CAPTURE_TO_RAW_FILE:

			if(FileCapture)
				CloseFileCapture();
			else
				OpenFileCapture(TRUE);

			break;

			/* Start/terminate the printer
			 * capture.
			 */

		case MEN_CAPTURE_TO_PRINTER:

			if(PrinterCapture)
				ClosePrinterCapture(TRUE);
			else
				OpenPrinterCapture(FALSE);

			break;

			/* Iconify the program. */

		case MEN_ICONIFY:

			if(Config->MiscConfig->ProtectiveMode)
			{
				if(Online && Config->MiscConfig->ReleaseDevice && !(Qualifier & SHIFT_KEY))
				{
					BlockWindows();

					if(!ShowRequest(Window,LocaleString(MSG_TERMMAIN_YOU_ARE_STILL_ONLINE_TXT),LocaleString(MSG_GLOBAL_YES_NO_TXT)))
					{
						ReleaseWindows();

						break;
					}

					ReleaseWindows();
				}
			}

			ActivateJob(MainJobQueue,IconifyJob);

			break;

			/* Say who we are. */

		case MEN_ABOUT:

			if(Qualifier & IEQUALIFIER_LSHIFT)
				StartTerminalTest(Qualifier);
			else
			{
				BlockWindows();
				ShowAbout(FALSE);
				ReleaseWindows();
			}

			break;

			/* Terminate the program. */

		case MEN_QUIT:

			if((Qualifier & SHIFT_KEY) || !Config->MiscConfig->ProtectiveMode)
				MainTerminated = TRUE;
			else
			{
				LONG OldLen,Len;
				STRPTR Buffer;

				OldLen = Len = strlen(LocaleString(MSG_TERMMAIN_REALLY_QUIT_TXT)) + 3;

				if(Online)
					Len += strlen(LocaleString(MSG_WAIT_PROGRAM_IS_STILL_ONLINE_TXT)) + 4;

				if(BufferChanged)
					Len += strlen(LocaleString(MSG_WAIT_REVIEW_BUFFER_NOT_SAVED_TXT)) + 4;

				if(ConfigChanged)
					Len += strlen(LocaleString(MSG_WAIT_CONFIGURATION_HAS_BEEN_CHANGED_TXT)) + 4;

				if(PhonebookChanged)
					Len += strlen(LocaleString(MSG_WAIT_PHONEBOOK_NOT_SAVED_TXT)) + 4;

				if(TranslationChanged)
					Len += strlen(LocaleString(MSG_WAIT_TRANSLATION_TABLES_CHANGED_TXT)) + 4;

				if(MacroChanged)
					Len += strlen(LocaleString(MSG_WAIT_MACRO_KEYS_CHANGED_TXT)) + 4;

				if(CursorKeysChanged)
					Len += strlen(LocaleString(MSG_WAIT_CURSOR_KEYS_CHANGED_TXT)) + 4;

				if(FastMacrosChanged)
					Len += strlen(LocaleString(MSG_WAIT_FAST_MACROS_CHANGED_TXT)) + 4;

				if(HotkeysChanged)
					Len += strlen(LocaleString(MSG_WAIT_HOTKEYS_CHANGED_TXT)) + 4;

				if(SpeechChanged)
					Len += strlen(LocaleString(MSG_WAIT_SPEECH_SETTINGS_CHANGED_TXT)) + 4;

				if(SoundChanged)
					Len += strlen(LocaleString(MSG_WAIT_SOUND_SETTINGS_CHANGED_TXT)) + 4;

				BlockWindows();

				ChangeWindowPtr(&OldPtr,(APTR)Window);

				if(OldLen != Len)
				{
					if(Buffer = (STRPTR)AllocVecPooled(Len,MEMF_ANY))
					{
						LimitedSPrintf(Len,Buffer,"%s\n\n",LocaleString(MSG_TERMMAIN_REALLY_QUIT_TXT));

						if(Online)
							FormatAppend(Buffer," · %s\n",LocaleString(MSG_WAIT_PROGRAM_IS_STILL_ONLINE_TXT));

						if(BufferChanged)
							FormatAppend(Buffer," · %s\n",LocaleString(MSG_WAIT_REVIEW_BUFFER_NOT_SAVED_TXT));

						if(ConfigChanged)
							FormatAppend(Buffer," · %s\n",LocaleString(MSG_WAIT_CONFIGURATION_HAS_BEEN_CHANGED_TXT));

						if(PhonebookChanged)
							FormatAppend(Buffer," · %s\n",LocaleString(MSG_WAIT_PHONEBOOK_NOT_SAVED_TXT));

						if(TranslationChanged)
							FormatAppend(Buffer," · %s\n",LocaleString(MSG_WAIT_TRANSLATION_TABLES_CHANGED_TXT));

						if(MacroChanged)
							FormatAppend(Buffer," · %s\n",LocaleString(MSG_WAIT_MACRO_KEYS_CHANGED_TXT));

						if(CursorKeysChanged)
							FormatAppend(Buffer," · %s\n",LocaleString(MSG_WAIT_CURSOR_KEYS_CHANGED_TXT));

						if(FastMacrosChanged)
							FormatAppend(Buffer," · %s\n",LocaleString(MSG_WAIT_FAST_MACROS_CHANGED_TXT));

						if(HotkeysChanged)
							FormatAppend(Buffer," · %s\n",LocaleString(MSG_WAIT_HOTKEYS_CHANGED_TXT));

						if(SpeechChanged)
							FormatAppend(Buffer," · %s\n",LocaleString(MSG_WAIT_SPEECH_SETTINGS_CHANGED_TXT));

						if(ShowRequest(Window,Buffer,LocaleString(MSG_GLOBAL_YES_NO_TXT)))
							MainTerminated = TRUE;

						FreeVecPooled(Buffer);
					}
					else
						MainTerminated = TRUE;
				}
				else
					MainTerminated = TRUE;

				RestoreWindowPtr(OldPtr);

				ReleaseWindows();
			}

			break;

			/* Feed the contents of the clipboard
			 * into the input stream.
			 */

		case MEN_PASTE:

			if(!OpenClip(Config->ClipConfig->ClipboardUnit))
			{
				ClipInput = TRUE;

				if(Qualifier & SHIFT_KEY)
					ClipXerox = TRUE;
			}
			else
				ClipInput = FALSE;

			break;

		case MEN_COPY:

			WindowMarkerTransfer(Qualifier);
			break;

		case MEN_CLEAR:

			WindowMarkerStop();
			break;

		case MEN_SELECT_ALL:

			WindowMarkerSelectAll();
			break;

			/* Execute an AmigaDOS command. */

		case MEN_EXECUTE_DOS_COMMAND:

			BlockWindows();

				/* Enter the name of the command. */

			if(GetString(TRUE,FALSE,0,LocaleString(MSG_TERMMAIN_ENTER_AMIGADOS_COMMAND_TXT),AmigaDOSCommandBuffer))
			{
				if(AmigaDOSCommandBuffer[0])
					LaunchCommandAsync(AmigaDOSCommandBuffer);
			}

			ReleaseWindows();

			break;

			/* Execute an ARexx script command. */

		case MEN_EXECUTE_REXX_COMMAND:

			BlockWindows();

				/* Get the rexx file name/program. */

			if(GetString(TRUE,FALSE,0,LocaleString(MSG_TERMMAIN_ENTER_AREXX_COMMAND_TXT),ARexxCommandBuffer))
			{
				if(ARexxCommandBuffer[0])
					LaunchRexxAsync(ARexxCommandBuffer);
			}

			ReleaseWindows();

			break;

			/* Turn recording on/off. */

		case MEN_RECORD:

			if(GetItem(MEN_RECORD))
			{
				if(!Recording)
				{
					if(CreateRecord(CurrentBBSName[0] ? CurrentBBSName : LocaleString(MSG_SCREENPANEL_UNKNOWN_TXT)))
					{
						RememberResetOutput();
						RememberResetInput();

						RememberOutput = TRUE;

						Recording = TRUE;
						RecordingLine = FALSE;

						PushStatus(STATUS_RECORDING);

						OnItem(MEN_RECORD_LINE);
					}
				}
			}
			else
			{
				if(Recording)
				{
					FinishRecord();

					RememberOutput = FALSE;
					RememberInput = FALSE;

					Recording = FALSE;
					RecordingLine = FALSE;

					PopStatus();

					OffItem(MEN_RECORD_LINE);
				}
			}

			break;

		case MEN_RECORD_LINE:

			if(Recording)
			{
				if(GetItem(MEN_RECORD))
				{
					if(!RecordingLine)
					{
						PopStatus();
						PushStatus(STATUS_RECORDING_LINE);

						RecordingLine = TRUE;

						RememberResetInput();

						RememberOutput = FALSE;
						RememberInput = TRUE;
					}
				}
				else
				{
					if(RecordingLine)
					{
						PopStatus();
						PushStatus(STATUS_RECORDING);

						RememberSpill();

						RecordingLine = FALSE;

						RememberOutput = TRUE;
						RememberInput = FALSE;
					}
				}
			}

			break;

		case MEN_DISABLE_TRAPS:

			Checked = GetItem(MEN_DISABLE_TRAPS);

			if(Checked || GenericListCount(GenericListTable[GLIST_TRAP]) == 0)
				WatchTraps = FALSE;
			else
				WatchTraps = TRUE;

			break;

			/* Edit the trap settings? */

		case MEN_EDIT_TRAPS:

			BlockWindows();

			TrapPanelConfig(Config,&TrapsChanged);

			CheckItem(MEN_DISABLE_TRAPS,!WatchTraps);

			ReleaseWindows();

			break;

			/* Open the phonebook and dial the
			 * list of entries the user will select.
			 */

		case MEN_PHONEBOOK:

			BlockWindows();

			if(PhonePanel(Qualifier))
			{
				if(AskDial(Window))
					DoDial = DIAL_LIST;
			}

			ReleaseWindows();

			break;

			/* Redial those dial list entries which
			 * we were unable to connect.
			 */

		case MEN_REDIAL:

			BlockWindows();

				/* If the modem is still online, provide help. */

			if(AskDial(Window))
				DoDial = DIAL_LIST;

			ReleaseWindows();

			break;

			/* Dial a single number. */

		case MEN_DIAL_NUMBER:

			BlockWindows();

				/* If the modem is still online, provide help. */

			if(AskDial(Window))
			{
				if(GetString(FALSE,FALSE,0,LocaleString(MSG_TERMMAIN_ENTER_PHONE_NUMBER_TXT),DialNumberBuffer))
				{
					if(DialNumberBuffer[0])
					{
							/* Remove all entries from the list. */

						DeleteDialList(GlobalPhoneHandle);

						if(AddDialEntry(GlobalPhoneHandle,NULL,DialNumberBuffer))
							DoDial = DIAL_LIST;
					}
				}
			}

			ReleaseWindows();

			break;

			/* Send a break across the serial line. */

		case MEN_SEND_BREAK:

			SendBreak();
			break;

			/* Hang up the phone line. */

		case MEN_HANG_UP:

			FullHangup(FALSE);

			break;

			/* Wait a bit... */

		case MEN_WAIT:

			Easy.es_StructSize		= sizeof(struct EasyStruct);
			Easy.es_Flags			= NULL;
			Easy.es_Title			= LocaleString(MSG_TERMAUX_TERM_REQUEST_TXT);
			Easy.es_GadgetFormat	= LocaleString(MSG_GLOBAL_CONTINUE_TXT);
			Easy.es_TextFormat		= LocaleString(MSG_TERMMAIN_WAITING_TXT);

			BlockWindows();

			if(ReqWindow = BuildEasyRequest(Window,&Easy,IDCMP_RAWKEY,NULL))
			{
				ULONG Signals,Mask,Which;
				LONG Seconds;
				BOOL More;

					/* Don't echo serial output. */

			/*	Quiet = TRUE; */

				if(!(Seconds = Config->MiscConfig->WaitDelay))
					Seconds = 1;

				SerialCommand(Config->MiscConfig->WaitString);
				RunJob(SerialJob);

				StartTime(Seconds,0);

				if(ReadPort && !Get_xOFF() && ProcessIO)
					Mask = SIG_SERIAL;
				else
					Mask = NULL;

				More = FALSE;

				FOREVER
				{
					Which = Mask | SIG_REXX | SIG_TIMER | PORTMASK(ReqWindow->UserPort);

					if(More)
					{
						Signals = SetSignal(0,Which) & Which;
						More = FALSE;
					}
					else
						Signals = Wait(Which);

					if(Signals & PORTMASK(ReqWindow->UserPort))
					{
						ULONG IDCMP = NULL;
						LONG Result;

						Result = SysReqHandler(ReqWindow,&IDCMP,FALSE);

						if(Result == 0 || (Result == -2 && !(IDCMP & IDCMP_RAWKEY)))
						{
							StopTime();

							SerialCommand(Config->MiscConfig->WaitString);

							break;
						}
					}

					if(Signals & SIG_TIMER)
					{
						WaitTime();

						SerialCommand(Config->MiscConfig->WaitString);

						StartTime(Seconds,0);
					}

					if(Signals & Mask)
					{
						More = RunJob(SerialJob);

						if(Get_xOFF())
						{
							Mask = NULL;
							More = FALSE;
						}
					}

					if(Signals & SIG_REXX)
					{
						struct RexxMsg *Msg;

						if(Msg = (struct RexxMsg *)GetMsg(TermRexxPort))
						{
							Msg->rm_Result1 = RC_ERROR;
							Msg->rm_Result2 = ERR10_001;

							ReplyMsg((struct Message *)Msg);

							More = TRUE;
						}
					}
				}

				RunJob(SerialJob);

			/*	Quiet = FALSE; */

				FreeSysRequest(ReqWindow);
			}

			ReleaseWindows();

			break;

			/* Flush the serial buffers. */

		case MEN_FLUSH_BUFFER:

			ClearSerial();

			RestartSerial();

			break;

			/* Release the serial device for other
			 * applications.
			 */

		case MEN_RELEASE_DEVICE:

			ActivateJob(MainJobQueue,ReleaseSerialJob);
			break;

		case MEN_UPLOAD_ASCII:

			BlockWindows();

			if(ChangeProtocol(Config->TransferConfig->ASCIIUploadLibrary,Config->TransferConfig->ASCIIUploadType))
				StartSendXPR_AskForFile(TRANSFER_ASCII,TRUE);

			ResetProtocol();

			ReleaseWindows();

			break;

		case MEN_DOWNLOAD_ASCII:

			BlockWindows();

			if(ChangeProtocol(Config->TransferConfig->ASCIIDownloadLibrary,Config->TransferConfig->ASCIIDownloadType))
				StartReceiveXPR_AskForFile(TRANSFER_ASCII,TRUE);

			ResetProtocol();

			ReleaseWindows();

			break;

		case MEN_UPLOAD_TEXT:

			BlockWindows();

#ifdef BUILTIN_ZMODEM
			if(UseInternalZModem)
			{
				InternalZModemTextUpload();

				ReleaseWindows();
				break;
			}
#endif	/* BUILTIN_ZMODEM */

			if(ChangeProtocol(Config->TransferConfig->TextUploadLibrary,Config->TransferConfig->TextUploadType))
				StartSendXPR_AskForFile(TRANSFER_TEXT,TRUE);

			ResetProtocol();

			ReleaseWindows();

			break;

		case MEN_DOWNLOAD_TEXT:

			BlockWindows();

#ifdef BUILTIN_ZMODEM
			if(UseInternalZModem)
			{
				ZReceive();

				ReleaseWindows();
				break;
			}
#endif	/* BUILTIN_ZMODEM */

			if(ChangeProtocol(Config->TransferConfig->TextDownloadLibrary,Config->TransferConfig->TextDownloadType))
				StartReceiveXPR_AskForFile(TRANSFER_TEXT,TRUE);

			ResetProtocol();

			ReleaseWindows();

			break;

			/* Edit and transfer a file. */

		case MEN_EDIT_AND_UPLOAD_TEXT:

			BlockWindows();

			if(!Config->PathConfig->Editor[0])
				GetString(TRUE,FALSE,0,LocaleString(MSG_TERMMAIN_ENTER_NAME_OF_EDITOR_TO_USE_TXT),Config->PathConfig->Editor);

			if(Config->PathConfig->Editor[0])
			{
				DummyBuffer[0] = 0;

				if(FileRequest = OpenSingleFile(Window,LocaleString(MSG_TERMMAIN_EDIT_AND_TRANSFER_FILE_TXT),LocaleString(MSG_TERMMAIN_EDIT_TXT),NULL,DummyBuffer,sizeof(DummyBuffer)))
				{
					UBYTE CompoundName[2 * MAX_FILENAME_LENGTH];

					FreeAslRequest(FileRequest);

					LimitedStrcpy(sizeof(CompoundName),CompoundName,Config->PathConfig->Editor);
					LimitedStrcat(sizeof(CompoundName),CompoundName," \"");
					LimitedStrcat(sizeof(CompoundName),CompoundName,DummyBuffer);
					LimitedStrcat(sizeof(CompoundName),CompoundName,"\"");

					LaunchCommand(CompoundName);

					BumpWindow(Window);

					if(GetFileSize(DummyBuffer))
					{
						switch(ShowRequest(Window,LocaleString(MSG_TERMMAIN_TRANSFER_FILE_AS_TXT),LocaleString(MSG_TERMMAIN_ASCII_UPLOAD_CANCEL_TXT),FilePart(DummyBuffer)))
						{
							case 1:

								if(ChangeProtocol(Config->TransferConfig->ASCIIUploadLibrary,Config->TransferConfig->ASCIIUploadType))
									StartSendXPR_File(TRANSFER_ASCII,DummyBuffer,TRUE);

								ResetProtocol();

								break;

							case 2:

								if(ChangeProtocol(Config->TransferConfig->TextUploadLibrary,Config->TransferConfig->TextUploadType))
									StartSendXPR_File(TRANSFER_TEXT,DummyBuffer,TRUE);

								ResetProtocol();

								break;
						}
					}
				}
			}

			ReleaseWindows();
			break;

		case MEN_UPLOAD_BINARY:

			BlockWindows();

#ifdef BUILTIN_ZMODEM
			if(UseInternalZModem)
			{
				InternalZModemBinaryUpload();

				ReleaseWindows();
				break;
			}
#endif	/* BUILTIN_ZMODEM */

			if(ChangeProtocol(Config->TransferConfig->BinaryUploadLibrary,Config->TransferConfig->BinaryUploadType))
				StartSendXPR_AskForFile(TRANSFER_BINARY,TRUE);

			ResetProtocol();

			ReleaseWindows();

			break;

			/* Download some files. */

		case MEN_DOWNLOAD_BINARY:

			BlockWindows();

#ifdef BUILTIN_ZMODEM
			if(UseInternalZModem)
			{
				ZReceive();

				ReleaseWindows();
				break;
			}
#endif	/* BUILTIN_ZMODEM */

			if(ChangeProtocol(Config->TransferConfig->BinaryDownloadLibrary,Config->TransferConfig->BinaryDownloadType))
				StartReceiveXPR_AskForFile(TRANSFER_BINARY,TRUE);

			ResetProtocol();

			ReleaseWindows();

			break;

			/* Clear the contents of the scrollback
			 * buffer.
			 */

		case MEN_CLEAR_BUFFER:

			if(Lines)
			{
				BlockWindows();

				if((Qualifier & SHIFT_KEY) || !Config->MiscConfig->ProtectiveMode)
				{
					FreeBuffer();

					TerminateBuffer();
				}
				else
				{
					if(ShowRequest(Window,LocaleString(MSG_TERMMAIN_BUFFER_STILL_HOLDS_LINES_TXT),LocaleString(MSG_GLOBAL_YES_NO_TXT),Lines))
					{
						FreeBuffer();

						TerminateBuffer();
					}
				}

				ReleaseWindows();
			}

			break;

			/* Display the scrollback buffer.
			 * Notify the scrollback task or
			 * fire it off if appropriate.
			 */

		case MEN_DISPLAY_BUFFER:

			if(!LaunchBuffer())
			{
				BlockWindows();

				ShowRequest(Window,LocaleString(MSG_TERMMAIN_UNABLE_TO_CREATE_BUFFER_TASK_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT));

				ReleaseWindows();
			}

			break;

			/* Close the buffer display. */

		case MEN_CLOSE_BUFFER:

			TerminateBuffer();
			break;

			/* Is the buffer to be frozen? */

		case MEN_FREEZE_BUFFER:

			BufferFrozen = GetItem(MEN_FREEZE_BUFFER);
			ConOutputUpdate();

			break;

			/* Load the buffer contents from a file. */

		case MEN_OPEN_BUFFER:

			BlockWindows();

			DummyBuffer[0] = 0;

			if(FileRequest = OpenSingleFile(Window,LocaleString(MSG_TERMMAIN_LOAD_BUFFER_TXT),LocaleString(MSG_GLOBAL_LOAD_TXT),NULL,DummyBuffer,sizeof(DummyBuffer)))
			{
				FreeAslRequest(FileRequest);

				if(GetFileSize(DummyBuffer))
				{
					if(SomeFile = Open(DummyBuffer,MODE_OLDFILE))
					{
						if(Lines)
						{
							switch(ShowRequest(Window,LocaleString(MSG_TERMMAIN_BUFFER_STILL_HOLDS_LINES_TXT),LocaleString(MSG_TERMMAIN_DISCARD_APPPEND_CANCEL_TXT),Lines))
							{
								case 0:

									Close(SomeFile);
									SomeFile = NULL;
									break;

								case 1:

									FreeBuffer();
									break;
							}
						}

						if(SomeFile)
						{
							LONG Len;

							LineRead(NULL,NULL,NULL);

							while((Len = LineRead(SomeFile,DummyBuffer,80)) > 0)
								CaptureParser(ParserStuff,DummyBuffer,Len,(COPTR)AddLine);

							Close(SomeFile);

							BufferChanged = TRUE;
						}
					}
					else
						ShowError(Window,ERR_LOAD_ERROR,IoErr(),DummyBuffer);
				}
			}

			ReleaseWindows();
			break;

			/* Save the contents of the scrollback
			 * buffer to a file (line by line).
			 */

		case MEN_SAVE_BUFFER_AS:

			BlockWindows();

			if(!Lines || !BufferLines)
				ShowRequest(Window,LocaleString(MSG_GLOBAL_NOTHING_IN_THE_BUFFER_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT));
			else
			{
				DummyBuffer[0] = 0;

				if(FileRequest = SaveFileOverwrite(Window,LocaleString(MSG_TERMMAIN_SAVE_BUFFER_TXT),NULL,NULL,DummyBuffer,sizeof(DummyBuffer)))
				{
					LONG Error = 0;

					FreeAslRequest(FileRequest);

					SomeFile = NULL;

						/* If the file we are about
						 * to create already exists,
						 * ask the user whether we are
						 * to create, append or skip
						 * the file.
						 */

					if(GetFileSize(DummyBuffer))
					{
						switch(ShowRequest(Window,LocaleString(MSG_GLOBAL_FILE_ALREADY_EXISTS_TXT),LocaleString(MSG_GLOBAL_CREATE_APPEND_CANCEL_TXT),DummyBuffer))
						{
							case 1:

								SomeFile = Open(DummyBuffer,MODE_NEWFILE);
								break;

							case 2:

								SomeFile = OpenToAppend(DummyBuffer,NULL);
								break;
						}
					}
					else
						SomeFile = Open(DummyBuffer,MODE_NEWFILE);

					if(SomeFile)
					{
						LONG i,Len;

							/* Obtain the semaphore required
							 * to gain access to the line buffer
							 */

						SafeObtainSemaphoreShared(&BufferSemaphore);

						for(i = 0 ; i < Lines ; i++)
						{
							Len = BufferLines[i][-1];

							if(Len)
							{
								SetIoErr(0);

								if(FWrite(SomeFile,BufferLines[i],Len,1) < 1)
								{
									Error = IoErr();

									break;
								}
							}

							SetIoErr(0);

							if(FPrintf(SomeFile,"\n") < 1)
							{
								Error = IoErr();

								break;
							}
						}

						ReleaseSemaphore(&BufferSemaphore);

						Close(SomeFile);

						AddProtection(DummyBuffer,FIBF_EXECUTE);

						if(Config->MiscConfig->CreateIcons)
							AddIcon(DummyBuffer,FILETYPE_TEXT,TRUE);

						BufferChanged = FALSE;
					}
					else
						Error = IoErr();

					if(Error)
						ShowError(Window,ERR_SAVE_ERROR,IoErr(),DummyBuffer);
				}
			}

			ReleaseWindows();

			break;

			/* Simply clear the screen and move the
			 * cursor to its home position.
			 */

		case MEN_CLEAR_SCREEN:

			ConClear();
			break;

			/* Reset the current text rendering font. */

		case MEN_RESET_FONT:

			ConResetFont();
			break;

			/* Reset the display styles and restore
			 * the colours.
			 */

		case MEN_RESET_STYLES:

			ConResetStyles();
			break;

			/* Reset the whole terminal. */

		case MEN_RESET_TERMINAL:

			ConResetTerminal();
			break;

		case MEN_SET_EMULATION:

			BlockWindows();

			if(XEmulatorBase && Config->TerminalConfig->EmulationMode == EMULATION_EXTERNAL)
			{
				OptionTitle = LocaleString(MSG_TERMMAIN_EMULATION_PREFERENCES_TXT);

				NewOptions = FALSE;

				XEmulatorOptions(XEM_IO);

				if(NewOptions)
				{
					SetEmulatorOptions(XEM_PREFS_SAVE);

					NewOptions = FALSE;
				}

				OptionTitle = NULL;
			}
			else
			{
				if(EmulationPanel(Window,Config))
				{
					ConfigSetup();

					ConfigChanged = TRUE;
				}
			}

			ReleaseWindows();

			break;

			/* Set the serial preferences. */

		case MEN_SERIAL:

			BlockWindows();

			if(SerialPanel(Window,Config))
			{
				ConfigSetup();

				ConfigChanged = TRUE;
			}

			ReleaseWindows();

			break;

			/* Set the modem preferences. */

		case MEN_MODEM:

			BlockWindows();

			if(ModemPanel(Window,Config))
			{
				ResetDataFlowFilter();

				ConfigChanged = TRUE;
			}

			ReleaseWindows();

			break;

			/* Set the screen preferences. */

		case MEN_SCREEN:

			BlockWindows();

			if(ScreenPanel(Window,Config))
			{
				if(memcmp(PrivateConfig->ScreenConfig->Colours,Config->ScreenConfig->Colours,sizeof(UWORD) * 16))
				{
					switch(Config->ScreenConfig->ColourMode)
					{
						case COLOUR_EIGHT:

							CopyMem(Config->ScreenConfig->Colours,ANSIColours,16 * sizeof(UWORD));
							break;

						case COLOUR_SIXTEEN:

							CopyMem(Config->ScreenConfig->Colours,EGAColours,16 * sizeof(UWORD));
							break;

						case COLOUR_AMIGA:

							CopyMem(Config->ScreenConfig->Colours,DefaultColours,16 * sizeof(UWORD));
							break;

						case COLOUR_MONO:

							CopyMem(Config->ScreenConfig->Colours,AtomicColours,16 * sizeof(UWORD));
							break;
					}
				}

				ConfigSetup();

				ConfigChanged = TRUE;
			}
			else
			{
				if(memcmp(PrivateConfig->ScreenConfig->Colours,Config->ScreenConfig->Colours,sizeof(UWORD) * 16))
				{
					switch(Config->ScreenConfig->ColourMode)
					{
						case COLOUR_EIGHT:

							CopyMem(Config->ScreenConfig->Colours,ANSIColours,16 * sizeof(UWORD));
							break;

						case COLOUR_SIXTEEN:

							CopyMem(Config->ScreenConfig->Colours,EGAColours,16 * sizeof(UWORD));
							break;

						case COLOUR_AMIGA:

							CopyMem(Config->ScreenConfig->Colours,DefaultColours,16 * sizeof(UWORD));
							break;

						case COLOUR_MONO:

							CopyMem(Config->ScreenConfig->Colours,AtomicColours,16 * sizeof(UWORD));
							break;
					}

					ConfigChanged = TRUE;
				}
			}

			ReleaseWindows();

			break;

			/* Set the terminal preferences. */

		case MEN_TERMINAL:

			BlockWindows();

			if(!TerminalPanel(Window,Config))
				ReleaseWindows();
			else
			{
				ReleaseWindows();

				Update_CR_LF_Translation();

				ConfigSetup();

				ConfigChanged = TRUE;

					/* Make the changes now */

				if(FixScreenSize && !ResetDisplay)
				{
					ScreenSizeStuff();

					ForceStatusUpdate();

					HandleMenuCode(NULL,MEN_RESET_TERMINAL,0);
				}
			}

			break;

			/* Set the clipboard preferences. */

		case MEN_CLIPBOARD:

			BlockWindows();

			if(ClipPanel(Window,Config))
			{
				ConfigSetup();

				ConfigChanged = TRUE;
			}

			ReleaseWindows();

			break;

			/* Set the capture preferences. */

		case MEN_CAPTURE:

			BlockWindows();

			if(CapturePanel(Window,Config))
			{
				ConOutputUpdate();

				ConfigSetup();

				ConfigChanged = TRUE;
			}

			ReleaseWindows();

			break;

			/* Set the command preferences. */

		case MEN_COMMANDS:

			BlockWindows();

			if(CommandPanel(Window,Config))
				ConfigChanged = TRUE;

			ReleaseWindows();

			break;

			/* Set the miscellaneous preferences. */

		case MEN_MISC:

			BlockWindows();

			if(MiscPanel(Window,Config))
			{
				ConfigSetup();

				ConfigChanged = TRUE;
			}

			ReleaseWindows();

			break;

			/* Set the path settings. */

		case MEN_PATH:

			BlockWindows();

			if(PathPanel(Window,Config))
				ConfigChanged = TRUE;

			ReleaseWindows();

			break;

			/* Set the file transfer options. */

		case MEN_TRANSFER:

			BlockWindows();

#ifdef BUILTIN_ZMODEM
			if(UseInternalZModem)
			{
				ZSettings();

				ReleaseWindows();

				break;
			}
#endif	/* BUILTIN_ZMODEM */

			XprIO->xpr_filename = NULL;

				/* Set up the library options. */

			if(XProtocolBase)
			{
				XPRCommandSelected = FALSE;

				ClearSerial();

				NewOptions = FALSE;

				TransferBits = XProtocolSetup(XprIO);

				RestartSerial();

				DeleteTransferPanel(TRUE);

					/* Successful? */

				if(!(TransferBits & XPRS_SUCCESS))
				{
					ShowRequest(Window,LocaleString(MSG_GLOBAL_FAILED_TO_SET_UP_PROTOCOL_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),LastXprLibrary);

					CloseLibrary(XProtocolBase);

					XProtocolBase = NULL;

					LastXprLibrary[0] = 0;

					TransferBits = 0;

					SetTransferMenu(TRUE);
				}
				else
					SaveProtocolOpts();
			}

			ReleaseWindows();

			break;

			/* Set the file transfer procol settings. */

		case MEN_TRANSFER_PROTOCOL:

			BlockWindows();

			if(LibPanel(Window,Config))
			{
				ConfigSetup();

				ConfigChanged = TRUE;

				ResetDataFlowFilter();
			}

			ReleaseWindows();

			break;

			/* Set the translation tables. */

		case MEN_TRANSLATION:

			BlockWindows();

			TranslationPanelConfig(Config,&SendTable,&ReceiveTable,LastTranslation,Window,&TranslationChanged);

				/* Choose the right console write routine. */

			ConOutputUpdate();

			ReleaseWindows();

			break;

			/* Set the keyboard macros. */

		case MEN_MACROS:

			BlockWindows();

			if(XEmulatorBase && Config->TerminalConfig->EmulationMode == EMULATION_EXTERNAL)
			{
				XEmulatorMacroKeyFilter(XEM_IO,NULL);

				MacroPanelConfig(Config,MacroKeys,LastMacros,Window,&MacroChanged);

				SetupXEM_MacroKeys(MacroKeys);
			}
			else
				MacroPanelConfig(Config,MacroKeys,LastMacros,Window,&MacroChanged);

			ReleaseWindows();

			break;

			/* Set the cursor keys. */

		case MEN_CURSORKEYS:

			BlockWindows();

			CursorPanelConfig(Config,CursorKeys,LastCursorKeys,Window,&CursorKeysChanged);

			ReleaseWindows();

			break;

			/* Set the fast macros. */

		case MEN_FAST_MACROS:

			BlockWindows();

			if(FastMacroPanelConfig(Config,&FastMacroList,LastFastMacros,Window,&FastMacrosChanged))
			{
				FastMacroCount = GetListSize(&FastMacroList);
				RefreshFastWindow();
			}

			ReleaseWindows();

			break;

			/* Set the hotkey preferences. */

		case MEN_HOTKEYS:

			BlockWindows();

			if(HotkeyPanelConfig(Config,&Hotkeys,LastKeys,&HotkeysChanged))
			{
				if(LastKeys[0] && !SetupCx())
					ShowRequest(Window,LocaleString(MSG_TERMMAIN_FAILED_TO_SET_UP_HOTKEYS_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT));
			}

			ReleaseWindows();

			break;

			/* Set the speech preferences. */

		case MEN_SPEECH:

			BlockWindows();

			SpeechPanelConfig(&SpeechConfig,LastSpeech,&SpeechChanged);

			ReleaseWindows();

			break;

			/* Set the sound preferences. */

		case MEN_SOUND:

			BlockWindows();

			if(SoundPanelConfig(&SoundConfig,LastSound,&SoundChanged))
			{
				if(LastSound[0])
					SoundInit();
			}

			ReleaseWindows();

			break;

			/* Edit the phone number patterns and rates. */

		case MEN_RATES:

			BlockWindows();

			PatternPanelConfig(PatternList,LastPattern,&PatternsChanged);

			ReleaseWindows();

			break;

			/* Open the preferences settings. */

		case MEN_OPEN_SETTINGS:

			BlockWindows();

			strcpy(DummyBuffer,LastConfig);

			if(FileRequest = OpenSingleFile(Window,LocaleString(MSG_TERMMAIN_OPEN_PREFERENCES_TXT),NULL,"#?.prefs",DummyBuffer,sizeof(DummyBuffer)))
			{
				FreeAslRequest(FileRequest);

				if(ReadConfig(DummyBuffer,PrivateConfig))
				{
					SwapConfig(PrivateConfig,Config);

					strcpy(LastConfig,DummyBuffer);

					ConfigSetup();

					ConfigChanged = FALSE;
				}
				else
					ShowRequest(Window,LocaleString(MSG_GLOBAL_ERROR_OPENING_FILE_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),DummyBuffer);
			}

			ReleaseWindows();

			break;

			/* Save the terminal preferences. */

		case MEN_SAVE_SETTINGS:

			if(LastConfig[0])
			{
				BlockWindows();

				if(!Screen)
					PutWindowInfo(WINDOW_MAIN,Window->LeftEdge,Window->TopEdge,Window->Width,Window->Height);

				if(!WriteConfig(LastConfig,Config))
					ShowError(Window,ERR_SAVE_ERROR,IoErr(),LastConfig);
				else
					ConfigChanged = FALSE;

				ReleaseWindows();

				break;
			}

			/* Save the terminal preferences to a
			 * given file name.
			 */

		case MEN_SAVE_SETTINGS_AS:

			BlockWindows();

			strcpy(DummyBuffer,LastConfig);

			if(FileRequest = SaveFile(Window,LocaleString(MSG_TERMMAIN_SAVE_PREFERENCES_AS_TXT),NULL,"#?.prefs",DummyBuffer,sizeof(DummyBuffer)))
			{
				FreeAslRequest(FileRequest);

				if(!Screen)
					PutWindowInfo(WINDOW_MAIN,Window->LeftEdge,Window->TopEdge,Window->Width,Window->Height);

				if(WriteConfig(DummyBuffer,Config))
				{
					strcpy(LastConfig,DummyBuffer);

					ConfigChanged = FALSE;
				}
				else
					ShowError(Window,ERR_SAVE_ERROR,IoErr(),DummyBuffer);
			}

			ReleaseWindows();

			break;

			/* Show terminal information window. */

		case MEN_STATUS_WINDOW:

			if(InfoWindow)
				CloseInfoWindow();
			else
				OpenInfoWindow();

			break;

		case MEN_REVIEW_WINDOW:

			if(ReviewWindow)
				DeleteReview();
			else
				CreateReview();

			break;

		case MEN_MATRIX_WINDOW:

			if(MatrixWindow)
				CloseMatrixWindow();
			else
				OpenMatrixWindow(Window);

			break;

			/* Open the packet window if necessary, else
			 * just activate it.
			 */

		case MEN_PACKET_WINDOW:

			CreatePacketWindow();
			break;

			/* Enable or disable the packet window */

		case MEN_CHAT_LINE:

			Checked = GetItem(MEN_CHAT_LINE);

			if(Checked != ChatMode)
			{
				ChatMode = Checked;

				ResetDisplay = TRUE;
				ActivateJob(MainJobQueue,ResetDisplayJob);
			}

			break;

			/* Toggle the presence of the fast! macro panel. */

		case MEN_FAST_MACROS_WINDOW:

			if(FastWindow)
				CloseFastWindow();
			else
				OpenFastWindow();

			break;

			/* Open the upload queue window. */

		case MEN_UPLOAD_QUEUE_WINDOW:

			CreateQueueProcess();
			break;

			/* Check if we should go for the quick dial menu */

		default:

			if(Code >= DIAL_MENU_LIMIT && ThisItem != NULL)
			{
				LONG i;

				if(ThisItem->Flags & CHECKED)
				{
					if(QuickDialMax < DIAL_MENU_MAX)
					{
						LONG Which;

						Which = -1;

						for(i = 0 ; i < QuickDialMax ; i++)
						{
							if(QuickDialTable[i] == Code)
							{
								Which = i;
								break;
							}
							else
							{
								if(QuickDialTable[i] == -1)
									Which = i;
							}
						}

						if(Which == -1)
							Which = QuickDialMax++;

						QuickDialTable[Which] = Code;
					}
				}
				else
				{
					for(i = 0 ; i < QuickDialMax ; i++)
					{
						if(QuickDialTable[i] == Code)
							QuickDialTable[i] = -1;
					}

					for(i = QuickDialMax - 1 ; i >= 0 ; i--)
					{
						if(QuickDialTable[i] == -1)
							QuickDialMax = i;
						else
							break;
					}
				}

				CheckDialMenu = TRUE;
			}

			break;
	}
}

	/* HandleReleaseSerial():
	 *
	 *	Release the serial device driver, then reopen it again.
	 */

BOOL
HandleReleaseSerial(JobNode *UnusedJob)
{
	if(!MainTerminated)
		LocalReleaseSerial();

	return(FALSE);
}

	/* HandleStartupFile(JobNode *Job):
	 *
	 *	Launches the startup ARexx script.
	 */

BOOL
HandleStartupFile(JobNode *UnusedJob)
{
	if(!MainTerminated)
	{
		LaunchRexxAsync(StartupFile);

		StartupFile[0] = 0;
	}

	return(FALSE);
}

BOOL
HandleIconifyJob(JobNode *UnusedJob)
{
	if(!MainTerminated)
		HandleIconify();

	return(FALSE);
}

BOOL
HandleRebuildMenuJob(JobNode *UnusedJob)
{
	if(!ResetDisplay && !MainTerminated)
	{
		struct Menu *Menu;

		if(Menu = BuildMenu())
			AttachMenu(Menu);
		else
			DisconnectDialMenu();

		RebuildMenu = FALSE;
	}

	return(FALSE);
}

BOOL
HandleResetDisplayJob(JobNode *UnusedJob)
{
	if(!MainTerminated)
	{
		if(!DisplayReset())
			MainTerminated = TRUE;
	}

	return(FALSE);
}

BOOL
HandleOnlineCleanupJob(JobNode *UnusedJob)
{
	if(!MainTerminated)
	{
		HandleOnlineCleanup(HungUp);

		WasOnline	= FALSE;
		HungUp		= FALSE;
	}

	return(FALSE);
}

BOOL
HandleDisplayCostJob(JobNode *Job)
{
	BOOL TurnOff = TRUE;

	if(!MainTerminated)
	{
		if(ResetDisplay)
			TurnOff = FALSE;
		else
		{
			UBYTE Sum[20];
			LONG ID;

				/* Reset the text rendering styles, font, etc. in
				 * order to keep the following text from getting
				 * illegible.
				 */

			SoftReset();

			if(DisplayPay)
			{
				if(DisplayHangup)
					ID = MSG_TERMMAIN_LOGMSG_HANG_UP_COST_TXT;
				else
					ID = MSG_TERMAUX_CARRIER_LOST_COST_TXT;

				CreateSum((DisplayPay + 5000) / 10000,TRUE,Sum,sizeof(Sum));

					/* Display how much we expect
					 * the user will have to pay for
					 * this call.
					 */

				ConPrintf(LocaleString(MSG_TERMMAIN_THIS_CALL_WILL_COST_YOU_TXT),Sum);
			}
			else
			{
				if(DisplayHangup)
					ID = MSG_TERMMAIN_LOGMSG_HANG_UP_TXT;
				else
					ID = MSG_TERMAUX_CARRIER_LOST_TXT;

				Sum[0] = 0;
			}

			LogAction(LocaleString(ID),Sum);

			DisplayHangup	= FALSE;
			DisplayPay		= 0;
		}
	}

	if(TurnOff)
		SuspendJob(MainJobQueue,Job);

	return(FALSE);
}
