/****************************************************************************
*
*	KeyMacro.c ------------	KeyMacro main process.
*
*	Author ----------------	Olaf Barthel, MXM
*				Brabeckstrasse 35
*				D-3000 Hannover 71
*
*	KeyMacro  ©  Copyright  1990  by  MXM;  Executable  program,
*	documentation  and  source  code are shareware.  If you like
*	this  program  a  small donation will entitle you to receive
*	updates and new programs from MXM.
*
****************************************************************************/

	/* Buffered IO include. */

#include "MRArpFile.h"

	/* Function prototypes. */

VOID			SimpleRequest(UBYTE *Args,...);
VOID			EraseGadget(struct RastPort *RPort,struct Gadget *Gadget,LONG Colour);
BYTE			PopRequest(struct Window *pr_ParentWindow,UBYTE *pr_TitleText,UBYTE *pr_BodyText,UBYTE *pr_PosText,UBYTE *pr_NegText,BYTE pr_Default);

UBYTE *			GetToken(UBYTE *s,LONG *start);
struct MacroKey *	AddMacroKey(struct MacroKey *MacroKey);
BYTE			Interprete(UBYTE *String,LONG Line);
BYTE			UpdateList(UBYTE *Name);
BYTE			GetDefaultKeyMap(VOID);
BYTE			UStrCmp(UBYTE *a,UBYTE *b);

VOID *			SendMacroMsg(struct MacroMessage *scm_Msg,struct MsgPort *scm_Port);
VOID *			AllocRem(LONG ByteSize,LONG Requirements);
VOID *			FreeRem(LONG *MemoryBlock);
ULONG			KeyInvert(UBYTE AnsiKey,struct InputEvent *Event,struct KeyMap *KeyMap,BYTE Depth);

VOID			main(int argc,UBYTE **argv);

	/* The Arp CLI-Interface data. */

UBYTE *CLI_Template	= "Startup,Q=Quit/s,D=Delay/k,P=Priority/k,I=Info/s";
UBYTE *CLI_Help		= "\nUsage: \33[1mKeyMacro\33[0m <Startup File> [Quit] [Delay <Microseconds>]\n                               [Priority <Input handler priority>] [Info]\n";

enum { ARG_STARTUP = 1, ARG_QUIT, ARG_DELAY, ARG_PRI, ARG_INFO };

	/* Easy macro. */

#define From_CLI	(ThatsMe -> pr_CLI)

	/* Global and shared data structures. */

extern struct ExecBase		*SysBase;
extern struct IntuitionBase	*IntuitionBase;
extern struct GfxBase		*GfxBase;
struct MSeg			*MSeg;
struct MacroKey			*KeyList;

	/* We use this list to identify the non-ascii keys. */

struct KeyAlias KeyTab[22] =
{
	{"TAB",		0x42},
	{"ESC",		0x45},
	{"SPACE",	0x40},
	{"RETURN",	0x44},
	{"ENTER",	0x43},
	{"DEL",		0x46},
	{"BACKSPACE",	0x41},
	{"HELP",	0x5F},
	{"LEFT",	0x4F},
	{"RIGHT",	0x4E},
	{"UP",		0x4C},
	{"DOWN",	0x4D},

	{"F1",		0x50},
	{"F2",		0x51},
	{"F3",		0x52},
	{"F4",		0x53},
	{"F5",		0x54},
	{"F6",		0x55},
	{"F7",		0x56},
	{"F8",		0x57},
	{"F9",		0x58},
	{"F10",		0x59}
};

	/* These are the qualifiers. */

struct KeyAlias QualifierTab[9] =
{
	{"NONE",	0},
	{"CTRL",	IEQUALIFIER_CONTROL},
	{"NUMPAD",	IEQUALIFIER_NUMERICPAD},
	{"LSHIFT",	IEQUALIFIER_LSHIFT},
	{"RSHIFT",	IEQUALIFIER_RSHIFT},
	{"LALT",	IEQUALIFIER_LALT},
	{"RALT",	IEQUALIFIER_RALT},
	{"LAMIGA",	IEQUALIFIER_LCOMMAND},
	{"RAMIGA",	IEQUALIFIER_RCOMMAND}
};

LONG __stdargs
CXBRK(VOID)
{
	return(0);
}

VOID
SimpleRequest(UBYTE *Args,...)
{
	UBYTE	 String[512];
	va_list	 VarArgs;

	va_start(VarArgs,Args);
	ArpVSPrintf(String,Args,VarArgs);

	PopRequest(NULL,(UBYTE *)"KeyMacro Request",(UBYTE *)String,NULL,NULL,FALSE);

	va_end(VarArgs);
}

VOID
EraseGadget(struct RastPort *RPort,struct Gadget *Gadget,LONG Colour)
{
	BYTE FgPen = RPort -> FgPen;

	SetAPen(RPort,Colour);
	RectFill(RPort,Gadget -> LeftEdge,Gadget -> TopEdge,Gadget -> LeftEdge + Gadget -> Width - 1,Gadget -> TopEdge + Gadget -> Height - 1);
	SetAPen(RPort,FgPen);
}

VOID
CalcDimensions(UBYTE *cd_String,LONG *cd_Width,LONG *cd_Height,LONG cd_MaxWidth)
{
	LONG i,cd_InLine = 0,cd_NumLines = 0;

	*cd_Width = *cd_Height = 0;

	for(i = 0 ; i < strlen((UBYTE *)cd_String) ; i++)
	{
		if(cd_String[i] == '\n' || cd_InLine == cd_MaxWidth)
		{
			if(cd_InLine > *cd_Width)
				*cd_Width = cd_InLine;

			cd_NumLines++;
			cd_InLine = 0;

			continue;
		}

		if(cd_String[i] == '\33')
		{
			while(cd_String[i] != 'm' && cd_String[i] != 'w' && i < strlen((UBYTE *)cd_String))
				i++;

			if(i >= strlen((UBYTE *)cd_String))
				i = strlen((UBYTE *)cd_String) - 1;

			continue;
		}

		if(cd_String[i] < ' ')
			continue;

		cd_InLine++;
	}

	*cd_Height = cd_NumLines;

	if(cd_InLine > *cd_Width)
		*cd_Width = cd_InLine;
}

BYTE
WriteConsole(struct Window *wc_Window,UBYTE *wc_String)
{
	struct IOStdReq	*wc_ConWrite;
	struct MsgPort	*wc_ConPort;

	BYTE wc_Success = FALSE;

	if(wc_ConPort = (struct MsgPort *)CreatePort(NULL,0))
	{
		if(wc_ConWrite = (struct IOStdReq *)CreateStdIO(wc_ConPort))
		{
			wc_ConWrite -> io_Data		= (APTR)wc_Window;
			wc_ConWrite -> io_Length	= sizeof(struct Window);

			if(!OpenDevice("console.device",0,wc_ConWrite,0))
			{
				wc_ConWrite -> io_Command	= CMD_WRITE;
				wc_ConWrite -> io_Data		= (APTR)"\2330 p";
				wc_ConWrite -> io_Length	= -1;

				DoIO(wc_ConWrite);

				wc_ConWrite -> io_Data		= (APTR)wc_String;
				wc_ConWrite -> io_Length	= -1;

				DoIO(wc_ConWrite);

				wc_Success = TRUE;

				CloseDevice(wc_ConWrite);
			}

			DeleteStdIO(wc_ConWrite);
		}

		DeletePort(wc_ConPort);
	}

	return(wc_Success);
}

BYTE
PopRequest(struct Window *pr_ParentWindow,UBYTE *pr_TitleText,UBYTE *pr_BodyText,UBYTE *pr_PosText,UBYTE *pr_NegText,BYTE pr_Default)
{
	STATIC struct NewWindow pr_StaticNewWindow =
	{
		0,0,
		0,1,
		0,1,
		VANILLAKEY | MOUSEBUTTONS | GADGETUP | CLOSEWINDOW | CLOSEWINDOW,
		RMBTRAP | WINDOWDRAG | WINDOWDEPTH | WINDOWCLOSE,
		(struct Gadget *)NULL,
		(struct Image *)NULL,
		(STRPTR)NULL,
		(struct Screen *)NULL,
		(struct BitMap *)NULL,
		0,0,
		0,0,
		WBENCHSCREEN
	};

	STATIC struct Gadget pr_StaticGadget =
	{
		(struct Gadget *)NULL,
		0,0,
		0,0,
		GADGHBOX,
		RELVERIFY | GADGIMMEDIATE,
		BOOLGADGET,
		(APTR)NULL,
		(APTR)NULL,
		(struct IntuiText *)NULL,
		NULL,
		(APTR)NULL,
		0,
		(APTR)NULL
	};

	STATIC struct TextAttr pr_TextAttr =
	{
		(UBYTE *)"topaz.font",
		8,
		FS_NORMAL,
		FPF_ROMFONT
	};

	struct NewWindow	*pr_NewWindow;
	struct Window		*pr_Window;
	struct IntuiMessage	*pr_IMsg;
	struct Gadget		*pr_PosGadget = NULL,*pr_NegGadget = NULL,*pr_TempGadget;

	struct Screen		 pr_Screen,*pr_FrontScreen;
	struct TextFont		*pr_TextFont;

	LONG			 pr_Width,pr_Height;
	BYTE			 pr_Result = FALSE;
	ULONG			 pr_IntuiLock;

	if(!pr_BodyText)
		return(pr_Result);

	if(!(pr_NewWindow = (struct NewWindow *)AllocMem(sizeof(struct NewWindow),MEMF_PUBLIC)))
		return(pr_Result);

	CopyMem(&pr_StaticNewWindow,pr_NewWindow,sizeof(struct NewWindow));

	pr_IntuiLock = LockIBase(NULL);

	pr_FrontScreen = IntuitionBase -> FirstScreen;

	UnlockIBase(pr_IntuiLock);

	if(pr_ParentWindow)
	{
		pr_NewWindow -> Type	= CUSTOMSCREEN;
		pr_NewWindow -> Screen	= pr_ParentWindow -> WScreen;
	}
	else
		OpenWorkBench();

	if(!GetScreenData(&pr_Screen,sizeof(struct Screen),pr_NewWindow -> Type,pr_NewWindow -> Screen))
	{
		FreeMem(pr_NewWindow,sizeof(struct NewWindow));
		return(pr_Result);
	}

	CalcDimensions(pr_BodyText,&pr_Width,&pr_Height,(pr_Screen . Width - 6) / 8);

	if(pr_Height > (pr_Screen . Height - 15 - 13) / 8)
	{
		FreeMem(pr_NewWindow,sizeof(struct NewWindow));
		return(pr_Result);
	}

	pr_NewWindow -> Width	= pr_Width * 8 + 6 + 4;
	pr_NewWindow -> Height	= pr_Height * 8 + GfxBase -> DefaultFont -> tf_YSize + 7 + 19 + 2;

	if(pr_TitleText)
		pr_NewWindow -> Title = pr_TitleText;
	else
		pr_NewWindow -> Title = (UBYTE *)"System Request";

	if(!pr_PosText && !pr_NegText)
		pr_NegText = (UBYTE *)"Continue?";

	if(pr_PosText)
	{
		if(!(pr_PosGadget = (struct Gadget *)AllocMem(sizeof(struct Gadget),MEMF_PUBLIC)))
		{
			FreeMem(pr_NewWindow,sizeof(struct NewWindow));
			return(pr_Result);
		}

		CopyMem(&pr_StaticGadget,pr_PosGadget,sizeof(struct Gadget));

		pr_PosGadget -> Width	= 8 * strlen((UBYTE *)pr_PosText) + 4;
		pr_PosGadget -> Height	= 8 + 2;

		pr_PosGadget -> LeftEdge= 3 + 2 + 3;
		pr_PosGadget -> TopEdge	= pr_NewWindow -> Height - 13 - 1;
	}

	if(pr_NegText)
	{
		if(!(pr_NegGadget = (struct Gadget *)AllocMem(sizeof(struct Gadget),MEMF_PUBLIC)))
		{
			FreeMem(pr_NewWindow,sizeof(struct NewWindow));

			if(pr_PosGadget)
				FreeMem(pr_PosGadget,sizeof(struct Gadget));

			return(pr_Result);
		}

		CopyMem(&pr_StaticGadget,pr_NegGadget,sizeof(struct Gadget));

		pr_NegGadget -> Width	= 8 * strlen((UBYTE *)pr_NegText) + 4;
		pr_NegGadget -> Height	= 8 + 2;

		pr_Width = pr_NegGadget -> Width + 6 + 4 + 6;

		if(pr_PosGadget)
			pr_Width += (pr_PosGadget -> Width + 12);

		if(pr_NewWindow -> Width < pr_Width)
			pr_NewWindow -> Width = pr_Width;

		pr_NegGadget -> LeftEdge= pr_NewWindow -> Width - pr_NegGadget -> Width - 3 - 2 - 3;
		pr_NegGadget -> TopEdge	= pr_NewWindow -> Height - 13 - 1;

		pr_NegGadget -> GadgetID= 1;
	}

	if(!pr_NegGadget && pr_NewWindow -> Width < pr_PosGadget -> Width + 6 + 4 + 6)
		pr_NewWindow -> Width = pr_PosGadget -> Width + 6 + 4 + 6;

	if(pr_Default && !pr_PosGadget)
		pr_Default = FALSE;

	if(!pr_Default && !pr_NegGadget)
		pr_Default = TRUE;

	if(pr_Default)
		pr_TempGadget = pr_PosGadget;
	else
		pr_TempGadget = pr_NegGadget;

	pr_NewWindow -> LeftEdge= pr_Screen . MouseX - (pr_TempGadget -> LeftEdge + pr_TempGadget -> Width / 2);
	pr_NewWindow -> TopEdge	= pr_Screen . MouseY - (pr_TempGadget -> TopEdge + pr_TempGadget -> Height / 2);

	while(pr_NewWindow -> LeftEdge < 0)
		pr_NewWindow -> LeftEdge++;

	while(pr_NewWindow -> TopEdge < 0)
		pr_NewWindow -> TopEdge++;

	while(pr_NewWindow -> LeftEdge + pr_NewWindow -> Width >= pr_Screen . Width)
		pr_NewWindow -> LeftEdge--;

	while(pr_NewWindow -> TopEdge + pr_NewWindow -> Height >= pr_Screen . Height)
		pr_NewWindow -> TopEdge--;

	if(!(pr_TextFont = (struct TextFont *)OpenFont(&pr_TextAttr)))
	{
		if(pr_PosGadget)
			FreeMem(pr_PosGadget,sizeof(struct Gadget));

		if(pr_NegGadget)
			FreeMem(pr_NegGadget,sizeof(struct Gadget));

		FreeMem(pr_NewWindow,sizeof(struct NewWindow));

		return(pr_Result);
	}

	if(!(pr_Window = (struct Window *)OpenWindow(pr_NewWindow)))
	{
		CloseFont(pr_TextFont);

		if(pr_PosGadget)
			FreeMem(pr_PosGadget,sizeof(struct Gadget));

		if(pr_NegGadget)
			FreeMem(pr_NegGadget,sizeof(struct Gadget));

		FreeMem(pr_NewWindow,sizeof(struct NewWindow));

		return(pr_Result);
	}

	SetFont(pr_Window -> RPort,pr_TextFont);

	SetAPen(pr_Window -> RPort,3);
	SetBPen(pr_Window -> RPort,2);
	SetDrMd(pr_Window -> RPort,JAM2);

	WriteConsole(pr_Window,pr_BodyText);

	if(pr_PosGadget)
	{
		AddGadget(pr_Window,pr_PosGadget,1);

		EraseGadget(pr_Window -> RPort,pr_PosGadget,2);

		Move(pr_Window -> RPort,pr_PosGadget -> LeftEdge + 2,pr_PosGadget -> TopEdge + 1 + pr_TextFont -> tf_Baseline);
		Text(pr_Window -> RPort,(UBYTE *)pr_PosText,strlen((UBYTE *)pr_PosText));
	}

	if(pr_NegGadget)
	{
		AddGadget(pr_Window,pr_NegGadget,1);

		EraseGadget(pr_Window -> RPort,pr_NegGadget,2);

		Move(pr_Window -> RPort,pr_NegGadget -> LeftEdge + 2,pr_NegGadget -> TopEdge + 1 + pr_TextFont -> tf_Baseline);
		Text(pr_Window -> RPort,(UBYTE *)pr_NegText,strlen((UBYTE *)pr_NegText));
	}

	MoveScreen(pr_Window -> WScreen,0,- pr_Window -> WScreen -> TopEdge);
	ScreenToFront(pr_Window -> WScreen);
	ActivateWindow(pr_Window);

Skip1:	FOREVER
	{
		ULONG pr_Class,pr_Code;
		struct Gadget *pr_Gadget;

		WaitPort(pr_Window -> UserPort);

		if(pr_IMsg = (struct IntuiMessage *)GetMsg(pr_Window -> UserPort))
		{
			pr_Class	= pr_IMsg -> Class;
			pr_Code		= pr_IMsg -> Code;
			pr_Gadget	= (struct Gadget *)pr_IMsg -> IAddress;

			if(pr_Class == VANILLAKEY)
				pr_Code = toupper(pr_Code);

			ReplyMsg(pr_IMsg);

			if(pr_Class == GADGETUP)
			{
				if(pr_Gadget -> GadgetID == 0)
					pr_Result = TRUE;

				break;
			}

			if(pr_Class == CLOSEWINDOW)
				break;

			if(pr_Class == VANILLAKEY)
			{
				pr_Code = toupper(pr_Code);

				if((pr_Code == 'Y' || pr_Code == 'J' || pr_Code == 'V' || pr_Code == 'C' || pr_Code == 'R' || pr_Code == '\r') && pr_PosText)
				{
					pr_Result = TRUE;
					break;
				}

				if((pr_Code == 'N' || pr_Code == 'Q' || pr_Code == 'B' || pr_Code == '\33') && pr_NegText)
					break;

				continue;
			}
		}
	}

	CloseFont(pr_TextFont);

	if(pr_PosGadget)
	{
		RemoveGadget(pr_Window,pr_PosGadget);
		FreeMem(pr_PosGadget,sizeof(struct Gadget));
	}

	if(pr_NegGadget)
	{
		RemoveGadget(pr_Window,pr_NegGadget);
		FreeMem(pr_NegGadget,sizeof(struct Gadget));
	}

	FreeMem(pr_NewWindow,sizeof(struct NewWindow));

	pr_IntuiLock = LockIBase(NULL);

	if(pr_FrontScreen == IntuitionBase -> FirstScreen)
		pr_FrontScreen = NULL;

	UnlockIBase(pr_IntuiLock);

	if(pr_FrontScreen)
		ScreenToFront(pr_FrontScreen);

	CloseWindow(pr_Window);

	return(pr_Result);
}

	/* UStrCmp():
	 *
	 *	strcmp function which ignores case.
	 */

BYTE
UStrCmp(UBYTE *a,UBYTE *b)
{
	for( ; ToUpper(*a) == ToUpper(*b) ; a++, b++)
	{
		if(!(*a))
			return(0);
	}

	return(1);
}

	/* GetToken(s,start):
	 *
	 *	Parse a string and split it into single tokens.
	 */

UBYTE *
GetToken(UBYTE *s,LONG *start)
{
	static UBYTE	buffer[256];
	LONG		i,end = 0,quote = FALSE,maxlen = strlen(s);
	UBYTE		t;

	if(maxlen > 255)
		maxlen = 255;

	if(*start > strlen(s) - 1 || !strlen(s) || !s)
		return(NULL);

	for(i = *start ; i <= maxlen ; i++)
	{
		if(!end && (s[i] == ' ' || s[i] == '\t'))
		{
			while((s[i] == ' ' || s[i] == '\t') && i < maxlen)
			{
				i++;
				(*start)++;
			}
		}

		t = s[i];

		if(!end && t == '+')
		{
			(*start)++;
			continue;
		}

		if(!end && t == '=')
		{
			strcpy(buffer,"=");
			(*start)++;

			return(buffer);
		}

		if(s[i] == '\\' && s[i + 1] == '\"')
		{
			i += 2;

			end = i - *start + 1;

			t = s[i];
		}

		if(t == '\"' && !quote)
		{
			quote = TRUE;

			(*start)++;

			end++;

			continue;
		}

		if((t == '+' || t == '=' || t == ' ' || t == '\t' || t == ';') && quote)
		{
			end++;
			continue;
		}

		if((t == '+' || t == '\n' || t == '=' || t == ' ' || t == 0) || (t == '\"' && quote) || (t == ';' && !quote))
		{
			if(t == ';' && !end)
				return(NULL);

			if(t == '\"')
			{
				strncpy(buffer,s + *start,end - 1);
				buffer[end - 1] = 0;
			}
			else
			{
				strncpy(buffer,s + *start,end);
				buffer[end] = 0;
			}

			(*start) += end;

			return(buffer);
		}

		end++;
	}

	return(NULL);
}

	/* AddMacroKey(MacroKey):
	 *
	 *	Add a macro key to the big list.
	 */

struct MacroKey	*
AddMacroKey(struct MacroKey *TheKey)
{
	struct MacroKey *MacroKey;

	if(MacroKey = (struct MacroKey *)AllocRem(sizeof(struct MacroKey),MEMF_PUBLIC|MEMF_CLEAR))
	{
		ObtainSemaphore(&MSeg -> MacroSemaphore);

		if(MSeg -> NumMacros == MSeg -> MaxMacros)
		{
			struct MacroKey **MacroList;

			if(MacroList = (struct MacroKey **)AllocRem((MSeg -> MaxMacros + 10) * sizeof(struct MacroKey *),MEMF_PUBLIC|MEMF_CLEAR))
			{
				if(MSeg -> MacroList)
				{
					LONG i;

					for(i = 0 ; i < MSeg -> NumMacros ; i++)
						MacroList[i] = MSeg -> MacroList[i];

					FreeRem(MSeg -> MacroList);
				}

				MSeg -> MacroList = MacroList;

				MSeg -> MaxMacros += 10;
			}
			else
			{
				FreeRem(MacroKey);

				return(NULL);
			}
		}

		MSeg -> MacroList[MSeg -> NumMacros++] = MacroKey;

		CopyMem(TheKey,MacroKey,sizeof(struct MacroKey));

		ReleaseSemaphore(&MSeg -> MacroSemaphore);

		return(MacroKey);
	}

	return(NULL);
}

	/* Interprete(String,Line):
	 *
	 *	Interprete a command line from the config file.
	 */

BYTE
Interprete(UBYTE *String,LONG Line)
{
	ULONG			 Qualifier = 0;
	ULONG			 Code = -1;
	struct InputEvent	 FakeEvent;
	struct MacroKey		 NewKey;

	LONG			 Start = 0,Key = FALSE,i,KeyCount = 0;
	volatile LONG		 QuitLoop;
	UBYTE			*Token,*CommandString,*WindowName = NULL,Recognized = FALSE;

	UBYTE			 KeyBuff1[40],KeyBuff2[40];

	if(String[strlen(String) - 1] == '\n')
		String[strlen(String) - 1] = 0;

	if(Token = GetToken(String,&Start))
	{
		if(!UStrCmp("KEY",Token))
			Key = TRUE;

		if(UStrCmp("COMMAND",Token) && !Key)
		{
			SimpleRequest("Line %ld: Unknown keyword:\n\n'%s'",Line,String);
			return(FALSE);
		}
	}
	else
		return(TRUE);

	FOREVER
	{
		if(Token = GetToken(String,&Start))
		{
			QuitLoop = TRUE;

			for(i = 0 ; i < 9 ; i++)
			{
				if(!UStrCmp(QualifierTab[i] . ka_Name,Token))
				{
					Recognized = TRUE;
					QuitLoop = FALSE;

					Qualifier |= QualifierTab[i] . ka_Key;
				}
			}
		}
		else
			break;

		if(QuitLoop)
			break;
	}

	if(!Recognized)
	{
		SimpleRequest("Line %ld: Didn't recognize qualifier:\n\n'%s'",Line,String);
		return(FALSE);
	}

	if(Token)
		goto JumpIn;

	if(Token = GetToken(String,&Start))
	{
JumpIn:		for(i = 0 ; i < 22 ; i++)
		{
			if(!UStrCmp(KeyTab[i] . ka_Name,Token))
			{
				Code = KeyTab[i] . ka_Key;
				goto Next;
			}
		}

		if(KeyInvert(Token[0],&FakeEvent,MSeg -> DefaultKeyMap,1))
			Code = FakeEvent . ie_Code;
	}

	if(Code == -1)
	{
		SimpleRequest("Line %ld: Didn't recognize key:\n\n'%s'",Line,String);
		return(FALSE);
	}

Next:	FOREVER
	{
		if(Token = GetToken(String,&Start))
		{
			if(!UStrCmp("=",Token))
				break;
		}
		else
		{
			SimpleRequest("Line %ld: Statement '=' missing:\n\n'%s'",Line,String);
			return(FALSE);
		}
	}

	if(Token = GetToken(String,&Start))
		strcpy(KeyBuff1,Token);
	else
	{
		SimpleRequest("Line %ld: Didn't find macro:\n\n'%s'",Line,String);
		return(FALSE);
	}

	if(Key)
		goto AddIt;

	if(!(Token = GetToken(String,&Start)))
		goto AddIt;

	if(UStrCmp("WINDOW",Token))
	{
		SimpleRequest("Line %ld: Didn't recognize 'WINDOW' statement:\n\n'%s'",Line,String);
		return(FALSE);
	}

	if(!(Token = GetToken(String,&Start)))
	{
		SimpleRequest("Line %ld: Didn't find window title:\n\n'%s'",Line,String);
		return(FALSE);
	}

	if(!(WindowName = (UBYTE *)AllocRem(strlen(Token) + 1,MEMF_PUBLIC)))
	{
		SimpleRequest("Can't allocate memory chunk!");
		return(FALSE);
	}

	strcpy(WindowName,Token);

AddIt:	for(i = 0 ; i < strlen(KeyBuff1) ; i++)
	{
		UBYTE c;

		if(KeyBuff1[i] != '\\')
		{
			KeyBuff2[KeyCount++] = KeyBuff1[i];
			continue;
		}

		if(i == strlen(KeyBuff1) - 1)
			break;

		i++;

		c = 0;

		switch(ToUpper(KeyBuff1[i]))
		{
			case 'U':	c = KC_CURSORUP;
					break;

			case 'D':	c = KC_CURSORDOWN;
					break;

			case 'L':	c = KC_CURSORLEFT;
					break;

			case 'R':	c = KC_CURSORRIGHT;
					break;

			case 'H':	c = KC_HELP;
					break;

			case 'B':	c = 8;
					break;

			case 'E':	c = 127;
					break;

			case 'F':	if(i == strlen(KeyBuff1) - 1)
						break;

					i++;

					if(!isdigit(KeyBuff1[i]))
						break;

					if(!KeyBuff1[i] == '1')
					{
						c = KC_FKEY1 + KeyBuff1[i] - '1';
						break;
					}

					if(i == strlen(KeyBuff1) - 1)
						break;

					i++;

					if(!isdigit(KeyBuff1[i]))
					{
						c = KC_FKEY1;
						break;
					}

					if(KeyBuff1[i] != '0')
						break;

					c = KC_FKEY10;
					break;

			case 'N':	c = '\n';
					break;

			case '\\':	c = '\\';
					break;

			default:	c = KeyBuff1[i];

					break;
		}

		if(c)
			KeyBuff2[KeyCount++] = c;
	}

	KeyBuff2[KeyCount] = 0;

	if(!(CommandString = (UBYTE *)AllocRem(strlen(KeyBuff2) + 1,MEMF_PUBLIC)))
	{
		SimpleRequest("Can't allocate memory chunk!");
		FreeRem(WindowName);

		return(FALSE);
	}

	strcpy(CommandString,KeyBuff2);

	memset(&NewKey,0,sizeof(struct MacroKey));

	NewKey . mk_CommandKey		= Code;
	NewKey . mk_CommandQualifier	= Qualifier;

	NewKey . mk_String		= CommandString;
	NewKey . mk_Window		= WindowName;

	if(Key)
		NewKey . mk_Type	= MK_WORD;
	else
		NewKey . mk_Type	= MK_COMMAND;

	if(AddMacroKey(&NewKey))
		return(TRUE);

	SimpleRequest("Line %ld: Key macro table full.",Line);
	return(FALSE);
}

	/* UpdateList(Name):
	 *
	 *	Update the big macro key list.
	 */

BYTE
UpdateList(UBYTE *Name)
{
	UBYTE		 LineBuff[257];
	LONG		 LineNum = 1;
	ARPFileHandle	*ConfigFile;

	if(!Name)
		Name = "S:KeyMacro.config";

	GetDefaultKeyMap();

	if(ConfigFile = OpenARPFile(Name,MODE_OLDFILE,976))
	{
		if(MSeg -> MacroList)
		{
			LONG i;

			ObtainSemaphore(&MSeg -> MacroSemaphore);

			for(i = 0 ; i < MSeg -> NumMacros ; i++)
			{
				if(MSeg -> MacroList[i])
				{
					if(MSeg -> MacroList[i] -> mk_String)
						FreeRem(MSeg -> MacroList[i] -> mk_String);

					if(MSeg -> MacroList[i] -> mk_Window)
						FreeRem(MSeg -> MacroList[i] -> mk_Window);

					FreeRem(MSeg -> MacroList[i]);
				}
			}

			FreeRem(MSeg -> MacroList);

			MSeg -> MacroList = NULL;

			MSeg -> NumMacros = MSeg -> MaxMacros = 0;

			ReleaseSemaphore(&MSeg -> MacroSemaphore);
		}

		while(FGetsARP(LineBuff,256,ConfigFile))
		{
			if(!Interprete(LineBuff,LineNum++))
			{
				CloseARPFile(ConfigFile);

				return(FALSE);
			}
		}

		CloseARPFile(ConfigFile);
	}
	else
	{
		SimpleRequest("Couldn't open configuration file!");
		FreeRem(KeyList);

		return(FALSE);
	}

	return(TRUE);
}

BYTE
GetDefaultKeyMap()
{
	struct IOStdReq	*ConsoleRequest;
	struct MsgPort	*ConsolePort;
	BYTE		 Result = FALSE;

	if(ConsolePort = CreatePort(NULL,0))
	{
		if(ConsoleRequest = CreateStdIO(ConsolePort))
		{
			if(!OpenDevice("console.device",CONU_STANDARD,ConsoleRequest,0))
			{
				ConsoleRequest -> io_Command	= CD_ASKDEFAULTKEYMAP;
				ConsoleRequest -> io_Length	= sizeof(struct KeyMap);
				ConsoleRequest -> io_Data	= (APTR)MSeg -> DefaultKeyMap;
				ConsoleRequest -> io_Flags	= IOF_QUICK;

				if(!DoIO(ConsoleRequest))
					Result = TRUE;

				CloseDevice(ConsoleRequest);
			}

			DeleteStdIO(ConsoleRequest);
		}

		DeletePort(ConsolePort);
	}

	return(Result);
}

	/* main(argc,argv):
	 *
	 *	The entry point to this program.
	 */

VOID
main(int argc,UBYTE **argv)
{
	struct Process	*ThatsMe = (struct Process *)SysBase -> ThisTask;
	UBYTE		*FileName = argv[ARG_STARTUP];
	LONG		 Created = FALSE;
	LONG		 i;

		/* Started from Workbench? */

	if(!From_CLI)
		FileName = NULL;

		/* Look if handler process is already running. */

	MSeg = (struct MSeg *)FindPort(PORTNAME);

		/* Short info? */

	if(argv[ARG_INFO])
	{
		Printf("\n\33[1m\33[33mKeyMacro\33[31m\33[0m the Amiga macro key handler.\n\n");

		Printf("         This program may be non-commercially\n");
		Printf("         redistributed!\n\n");

		Printf("\33[1m\33[33mAuthor\33[31m\33[0m - Olaf Barthel, MXM\n");
		Printf("         Brabeckstrasse 35\n");
		Printf("         D-3000 Hannover 71\n\n");

		Printf("	 Federal Republic of Germany.\n\n");

		exit(RETURN_OK);
	}

		/* Remove the handler? */

	if(argv[ARG_QUIT])
	{
		Printf("Removing \33[1m\33[33mKeyMacro\33[31m\33[0m, ");

		if(!MSeg)
		{
			Printf("failed!\a\n");

			exit(RETURN_OK);
		}

		MSeg -> Father = (struct Task *)SysBase -> ThisTask;

		if(MSeg -> Child)
		{
			Signal(MSeg -> Child,SIG_CLOSE);

			Wait(SIG_CLOSE);
		}

		RemPort(&MSeg -> Port);

		FreeMem(MSeg -> Port . mp_Node . ln_Name,sizeof(PORTNAME));

		if(MSeg -> Segment)
			UnLoadSeg(MSeg -> Segment);

		if(MSeg -> MacroList)
		{
			for(i = 0 ; i < MSeg -> NumMacros ; i++)
			{
				if(MSeg -> MacroList[i])
				{
					if(MSeg -> MacroList[i] -> mk_String)
						FreeRem(MSeg -> MacroList[i] -> mk_String);

					if(MSeg -> MacroList[i] -> mk_Window)
						FreeRem(MSeg -> MacroList[i] -> mk_Window);

					FreeRem(MSeg -> MacroList[i]);
				}
			}

			FreeRem(MSeg -> MacroList);
		}

		FreeRem(MSeg -> DefaultKeyMap);
		FreeRem(MSeg);

		Printf("OK.\n");

		exit(RETURN_OK);
	}

		/* Allocate the handler data. */

	if(!MSeg)
	{
		if(MSeg = (struct MSeg *)AllocRem(sizeof(struct MSeg),MEMF_PUBLIC | MEMF_CLEAR))
		{
			MSeg -> Port . mp_Flags			= PA_IGNORE;
			MSeg -> Port . mp_Node . ln_Pri		= 0;
			MSeg -> Port . mp_Node . ln_Type	= NT_MSGPORT;
			MSeg -> Port . mp_Node . ln_Name	= AllocMem(sizeof(PORTNAME),MEMF_PUBLIC);
			MSeg -> Child				= NULL;
			MSeg -> Father				= (struct Task *)SysBase -> ThisTask;
			MSeg -> SegSize				= sizeof(struct MSeg);
			MSeg -> RingBack			= SIGBREAKF_CTRL_C;
			MSeg -> Revision			= REVISION;

			if(argv[ARG_PRI])
				MSeg -> Pri = Atol(argv[ARG_PRI]);
			else
				MSeg -> Pri = 51;

			NewList(&MSeg -> Port . mp_MsgList);

			InitSemaphore(&MSeg -> MacroSemaphore);

			if(From_CLI)
			{
				Printf("\33[1m\33[33mKeyMacro v1.%ld \33[31m\33[0m© Copyright 1989, 1990 by \33[4mMXM\33[0m, All rights reserved.\n",REVISION);

				Printf("Installing \33[33m\33[1mKeyMacro\33[0m\33[31m, ");
			}

			if(MSeg -> Port . mp_Node . ln_Name)
				strcpy(MSeg -> Port . mp_Node . ln_Name,PORTNAME);
			else
			{
Failed:				FreeRem(MSeg);

				if(From_CLI)
					Printf("failed!\a\n");

				exit(RETURN_FAIL);
			}

			if(!(MSeg -> DefaultKeyMap = (struct KeyMap *)AllocPub(sizeof(struct KeyMap))))
				goto Failed;

			if(!(MSeg -> Segment = LoadSeg("KeyMacro-Handler")))
				MSeg -> Segment = LoadSeg("L:KeyMacro-Handler");

			if(!MSeg -> Segment)
			{
				if(From_CLI)
					Printf("unable to find \33[33mL:KeyMacro-Handler\33[31m\a!\n");

				FreeRem(MSeg -> DefaultKeyMap);
				FreeRem(MSeg -> Port . mp_Node . ln_Name);
				FreeRem(MSeg);

				exit(RETURN_FAIL);
			}
			else
			{
				AddPort(&MSeg -> Port);

				if(!CreateProc("KeyMacro-Handler",10,MSeg -> Segment,4096))
					goto NoMem;

				Wait(SIGBREAKF_CTRL_C);

				if(!MSeg -> Child)
				{
NoMem:					if(From_CLI)
						Printf("\33[33mFAILED!\33[31m (care to retry?)\n");

					RemPort(&MSeg -> Port);

					FreeRem(MSeg -> DefaultKeyMap);
					FreeRem(MSeg -> Port . mp_Node . ln_Name);

					if(MSeg -> Segment)
						UnLoadSeg(MSeg -> Segment);

					FreeRem(MSeg);

					exit(RETURN_FAIL);
				}
				else
				{
					if(From_CLI)
						Printf("initializing, ");

					GetDefaultKeyMap();

					if(From_CLI)
						Puts("Okay.");
					else
						SimpleRequest("\33[1mKeyMacro\33[0m installed.");

					Created = TRUE;
				}
			}
		}
	}

	if(argv[ARG_DELAY])
	{
		LONG Value = Atol(argv[ARG_DELAY]);

		if(Value)
		{
			if(Value < 10000)
			{
				if(From_CLI)
					Printf("\33[1mKeyMacro:\33[0m Delay value too small - must be >= 10000!\n");

				exit(RETURN_ERROR);
			}
		}

		MSeg -> Delay = Value;
	}

		/* Update the macro key list. */

	if(!Created && From_CLI)
	{
		Printf("\33[1mKeyMacro:\33[0m Updating macro keys... ");

		if(!UpdateList(FileName))
		{
			Printf("FAILED!\a\n");
			exit(RETURN_ERROR);
		}
		else
			Printf("Done.\n");
	}
	else
	{
		if(!UpdateList(FileName))
			exit(RETURN_ERROR);
	}

	exit(RETURN_OK);
}
