/*
 *	Parse.c - Copyright © 1990 by S.R. & P.C.
 *
 *	Created:	16 Jun 1990
 *	Modified:	28 Jul 1991  18:22:05
 *
 *	Make>> make
 */

#include "ParMBase.h"

extern struct Extended_MenuItem *AllocItem(struct ParMConfig *PCfg, char *itemstr);
extern BOOL AddMenu(struct ParMConfig *PCfg, char *str);
extern BOOL AddSubMenu(struct ParMConfig *PCfg, char *substr);
extern void EndSubMenu(struct ParMConfig *PCfg);
extern BOOL AddEntry(struct ParMConfig *PCfg, char *item, char *cmd, char *args, char *win, char shortcut, char mode, long stk, short pri);
extern BOOL AddParMEvent(struct Window *Win, USHORT Code, USHORT Qual, USHORT Flags);


struct StaticData {
	char *FileBuffer, *CurrentLine, *NextLine, *Byte, *LastByte;
	short LineNum;
	char tok[200];
};


/*****				 local variables					*****/

static char *KeyWordTab[] = {
	NULL,
	"ITEM",
	"ARUN",
	"RUN",
	"SHELL",
	"WB",
	"CFG",
	"WIN",
	"PRI",
	"STACK",
	"MENU",
	"SUBMENU",
	"ENDSUBMENU",
	"COLOR",
	"CMDWIN",
	"SHELLWIN",
	"SHELLCMD",
	"WAITCMD",
	"TMPDIR",
	"SHORTCUTQUAL",
	"ACTIVATEKEY",
	"PASSTHROUGH",
	"SCREENTOFRONT",
	"NOCHECK"
};

/* status  */

#define STAT_BEGIN		0
#define STAT_MENU		1
#define STAT_SUBMENU	2
#define STAT_ITEM		4



/* add the position of the error to the error message */

static void Err(struct ParMConfig *PCfg, struct StaticData *SD, char *msg)
{
	SimpleRequest(PCfg->ReqTitle, "%s\nLine %d, Char %ld", msg, SD->LineNum, SD->Byte-SD->CurrentLine+1);
}

#define SYNTAX(msg) { Err(PCfg, SD, msg); return FALSE; }


/*
 *	Parse a line that may contain semicolons. Backslash ('\') is the override
 *	char. This function is called from ParseConfig() and from Command().
 */

void ParseLine(char *cmd)
{
	struct ParMBase *ParMBase;
	char *s,*d,c;

	s = d = cmd;
	while (c = *d++ = *s++) {
		if (c == '\\')
			*(d-1) = *s++;
		else if (c == ';')
			*(d-1) = '\n';
	}
}


static BOOL get_line(struct StaticData *SD)
{
	register char *s,c;

	s = SD->CurrentLine = SD->NextLine;
	if (!*s)
		return FALSE;
	while ((c = *s++) != 10 && c);
	if (c)
		*(s-1) = '\0';
	SD->NextLine = s;
	SD->LineNum++;
	SD->Byte = SD->CurrentLine;
	return TRUE;
}


/*
 *	b is a register variable used to replace global Byte pointer in
 *	get_token() body. Then, Byte must be restored before leaving.
 */

static char get_token(struct StaticData *SD)
{
	register char *p, *b, c;
	short i;
	char quote;

	b = SD->Byte;
	while ((c = *b) == ' ' || c == '\t')
		b++;			/* skip extra spaces */
	if (c == '#')		/* comment */
		return 0;
	SD->LastByte = b;	/* save address of next token */
	if (c < 32 || c == '{' || c == '}') {
		SD->Byte = (c) ? b+1 : b;	/* prevent skipping of sentinel */
		return c;					/* '{', '}', '\0', or invalid char */
	}
	/* scan string */
	p = SD->tok;
	if (c == '"') {
		b++;
		quote = TRUE;
	}
	else
		quote = FALSE;
	while ((quote && *b != '"' && *b) || (!quote && *b > 32 && *b != ';'))
		*p++ = *b++;
	*p = '\0';
	if (quote)
		b++;	/* skip closing '"' */
	for (i = 1; i <= MAX_KEYWORD; i++) {
		if (!Strcmp(SD->tok, KeyWordTab[i]))
			break;	/* arp Strcmp() is not case sensitive */
	}
	SD->Byte = b;
	return i;
}


static BOOL ParseConfig(struct ParMConfig *PCfg, struct StaticData *SD)
{
	char t, shortcut, mode;
	long stack;
	short pri, i;
	USHORT status = STAT_BEGIN, Flags, Key[2];
	char *args, *cmd, *win;
	char itemstr[80], cmdstr[100], winstr[80];

	FreeMenus(PCfg);
	while(get_line(SD)) {
		switch (t = get_token(SD)) {
		case 0:		/* allow empty lines */
			break;
		case TOK_MENU:
			if (!(status & (STAT_MENU | STAT_ITEM)) && status != STAT_BEGIN)
				SYNTAX("Unexpected MENU")
			status = STAT_MENU;
			if (get_token(SD))
				AddMenu(PCfg, SD->tok);
			else
				SYNTAX("Unexpected end of line");
			break;
		case TOK_SUBMENU:
			if (!(status & STAT_MENU) || (status & STAT_SUBMENU))
				SYNTAX("Unexpected SUBMENU")
			status = STAT_SUBMENU;
			if (get_token(SD))
				AddSubMenu(PCfg, SD->tok);
			else
				SYNTAX("Unexpected end of line");
			break;
		case TOK_ENDSUBMENU:
			if (!(status & STAT_SUBMENU) || !(status & STAT_ITEM))
				SYNTAX("Unexpected ENDSUBMENU")
			PCfg->CurrentItem = &PCfg->CurrentSubMenu->NextItem;
			status = STAT_MENU | STAT_ITEM;
			break;
		case TOK_ITEM:
			if (status == STAT_BEGIN)
				SYNTAX("Unexpected ITEM")
			status |= STAT_ITEM;
			shortcut = pri = 0;
			stack = PCfg->DefaultStack;
			win = args = NULL;
			cmd = cmdstr;
			if (get_token(SD) == '{') {	/* command char */
				shortcut = *SD->Byte++;
				if (get_token(SD) != '}')
					SYNTAX("Missing closing '}'");
				get_token(SD);
			}
			strcpy(itemstr, SD->tok);
			switch (mode = get_token(SD)) {
			case TOK_WB:
				stack = 0;	/* Tell WBRun to take icon stack as default */
			case TOK_SHELL:
			case TOK_ARUN:
			case TOK_RUN:
				while ((t = get_token(SD))==TOK_WIN || t==TOK_STACK || t==TOK_PRI) {
					if (!get_token(SD))
						SYNTAX("Unexpected end of line")
					switch (t) {
					case TOK_WIN:
						if (mode == TOK_RUN || mode == TOK_WB)
							SYNTAX("WIN not allowed in this mode")
						strcpy(winstr, SD->tok);
						win = winstr;
						break;
					case TOK_STACK:
						stack = Atol(SD->tok);
						if (IoErr() || stack < 2000)
							SYNTAX("Invalid stack")
						break;
					case TOK_PRI:
						pri = Atol(SD->tok);
						if (IoErr() || pri < -128 || pri > 127)
							SYNTAX("Priority out of range")
						break;
					}
				}
				if (!t)
					SYNTAX("Unexpected end of line")
				switch(mode) {
				case TOK_ARUN:
					args = SD->Byte;
				case TOK_WB:
					strcpy(cmdstr, SD->tok);
					break;
				default:	/* RUN & SHELL modes */
					ParseLine(SD->LastByte);
					cmd = SD->LastByte;
				}
				break;
			case TOK_CFG:
				if (!get_token(SD))
					SYNTAX("Unexpected end of line")
				strcpy(cmdstr, SD->tok);
				break;
			default:
				SYNTAX("WB, ARUN, RUN, SHELL or CFG Expected")
			}
			if (!AddEntry(PCfg, itemstr, cmd, args, win, shortcut, mode, stack, pri))
				return FALSE;
			break;
		case TOK_ACTIVATEKEY:
			Flags = 0;
			while ((t = get_token(SD))==TOK_PASSTHROUGH || t==TOK_SCREENTOFRONT || t==TOK_NOCHECK) {
				switch (t) {
				case TOK_PASSTHROUGH:
					Flags |= PEF_PASSTHROUGH;
					break;
				case TOK_SCREENTOFRONT:
					Flags |= PEF_SCREENTOFRONT;
					break;
				case TOK_NOCHECK:
					Flags |= PEF_NOCHECK;
					break;
				}
			}
			for( i=0 ; i<2 ; i++ ) {
				if (t) {
					Key[i] = Atol(SD->tok);
					if (IoErr())
						SYNTAX("Invalid Key")
				}
				else
					SYNTAX("Unexpected end of line")
				t = get_token(SD);
			}
			/* Key[0] = Qualifier, Key[1] = Code */
			if (!AddParMEvent(PCfg->Win, Key[1], Key[0], Flags))
				Err(PCfg, SD, "Key already used");
			break;
		case TOK_STR:
			SYNTAX("Keyword expected")
			break;
		default:
			if (get_token(SD)) {
				switch(t) {
				case TOK_COLOR:
					/* control if blockpen and detailpen are different */
					PCfg->MenuPen = ((PCfg->MenuPen = Atol(SD->tok)) == PCfg->Win->BlockPen) ? PCfg->Win->DetailPen : PCfg->MenuPen;
					break;
				case TOK_CMDWIN:
					strcpy(PCfg->CmdWindow, SD->tok);
					break;
				case TOK_SHELLWIN:
					strcpy(PCfg->ShellWindow, SD->tok);
					break;
				case TOK_SHELLCMD:
					strcpy(PCfg->ShellCmd, SD->LastByte);
					break;
				case TOK_WAITCMD:
					strcpy(PCfg->WaitCmd, SD->LastByte);
					break;
				case TOK_TMPDIR:
					strcpy(PCfg->TmpDir, SD->tok);
					break;
				case TOK_SHORTCUTQUAL:
					PCfg->ShortCutQual = (USHORT)Atol(SD->tok);
					if (IoErr()) {
						PCfg->ShortCutQual = 0;
						SYNTAX("Invalid qualifier");
					}
					break;
				}
			}
			else
				SYNTAX("Missing argument")
		}
	}
	return TRUE;
}


long ParseMenus(struct ParMConfig *PCfg)
{
	struct ParMBase *ParMBase;
	struct StaticData StaticData;
	BPTR cfg;
	long FileSize;
	BOOL stat;
	BOOL pb = TRUE;

	if (cfg = Open(PCfg->CurCfg, MODE_OLDFILE)) {
		Seek(cfg, 0, OFFSET_END);
		if ((FileSize = Seek(cfg, 0, OFFSET_BEGINNING)) > 0) {
			if (StaticData.FileBuffer = AllocMem(FileSize+1, MEMF_PUBLIC|MEMF_CLEAR)) {
				if (Read(cfg, StaticData.FileBuffer, FileSize) == FileSize) {
					StaticData.NextLine = StaticData.FileBuffer;
					StaticData.LineNum = 0;
					stat = ParseConfig(PCfg, &StaticData);
					CleanUp(PCfg->LinkMenu->NextMenu, PCfg->LinkMenu->LeftEdge + PCfg->LinkMenu->Width, PCfg->ItemHeight);
					pb = FALSE;
				}
				Close(cfg);
				FreeMem(StaticData.FileBuffer, FileSize+1);
			}
		}
	}
	if (pb) {
		SimpleRequest(PCfg->ReqTitle, "Couldn't open/read \"%s\"", PCfg->CurCfg);
		return FALSE;
	}
	return stat;
}


