/*
 *	Parse.c - Copyright © 1990 by S.R. & P.C.
 *
 *	Created:	16 Jun 1990
 *	Modified:	09 Dec 1990  17:38:26
 *
 *	Make>> make
 */

#include "Tokens.h"


/*****				external functions					*****/

extern void AddMenu(char *);
extern void AddSubMenu(char *);
extern void AddEntry(char *, char *, char *, char *, char, char, long, short);
extern void EndSubMenu(void);
extern void FreeMenus(void);
extern void CleanUp(void);


/*****				 global functions					*****/

BOOL ParseMenus(void);


/*****				 global variables					*****/

extern struct Window *Win;
extern struct Menu Menu1;
extern char CurCfg[];
extern char CmdWindow[];
extern char ShellWindow[];
extern char ShellCmd[];
extern char WaitCmd[];
extern char TmpDir[];
extern long DefaultStack;
extern UBYTE menu_pen;
extern char *ReqTitle;

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

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

/* status  */

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

static char *FileBuffer, *CurrentLine, *NextLine, *Byte, *LastByte;
static short LineNum;
static char tok[200];


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

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

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


static BOOL get_line(void)
{
	register char *s,c;

	s = CurrentLine = NextLine;
	if (!*s)
		return FALSE;
	while ((c = *s++) != 10 && c);
	if (c)
		*(s-1) = '\0';
	NextLine = s;
	LineNum++;
	Byte = 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(void)
{
	register char *p, *b, c;
	short i;
	char quote;

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


/*
 *	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)
{
	register char *s,*d,c;

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


static BOOL ParseConfig(void)
{
	char t, shortcut, mode;
	long stack;
	short pri;
	USHORT status = STAT_BEGIN;
	char *args, *cmd, *win;
	char itemstr[80], cmdstr[100], winstr[80];

	FreeMenus();
	while( get_line() ) {
		switch (t = get_token()) {
		case TOK_MENU:
			if (!(status & (STAT_MENU | STAT_ITEM)) && status != STAT_BEGIN)
				SYNTAX("Unexpected MENU")
			status = STAT_MENU;
			if (get_token())
				AddMenu(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())
				AddSubMenu(tok);
			else
				SYNTAX("Unexpected end of line");
			break;
		case TOK_ENDSUBMENU:
			if (!(status & STAT_SUBMENU) || !(status & STAT_ITEM))
				SYNTAX("Unexpected ENDSUBMENU")
			EndSubMenu();
			status = STAT_MENU | STAT_ITEM;
			break;
		case TOK_ITEM:
			if (status == STAT_BEGIN)
				SYNTAX("Unexpected ITEM")
			status |= STAT_ITEM;
			shortcut = pri = 0;
			stack = DefaultStack;
			win = args = NULL;
			cmd = cmdstr;
			if (get_token() == '{') {	/* command char */
				shortcut = *Byte++;
				if (get_token() != '}')
					SYNTAX("Missing closing '}'");
				get_token();
			}
			strcpy(itemstr, tok);
			switch (mode = get_token()) {
			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())==TOK_WIN || t==TOK_STACK || t==TOK_PRI) {
					if (!get_token())
						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, tok);
						win = winstr;
						break;
					case TOK_STACK:
						stack = Atol(tok);
						if (IoErr() || stack < 2000)
							SYNTAX("Invalid stack")
						break;
					case TOK_PRI:
						pri = Atol(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 = Byte;
				case TOK_WB:
					strcpy(cmdstr, tok);
					break;
				case TOK_SHELL:
					ParseLine(LastByte);
				default:	/* RUN mode */
					cmd = LastByte;
				}
				break;
			case TOK_CFG:
				if (!get_token())
					SYNTAX("Unexpected end of line");
				strcpy(cmdstr, tok);
				break;
			default:
				Err("WB, ARUN, RUN, SHELL or CFG Expected");
				return FALSE;
			}
			AddEntry(itemstr, cmd, args, win, shortcut, mode, stack, pri);
			break;
		case TOK_COLOR:
			get_token();
			menu_pen = ((menu_pen = Atol(tok)) == Win->BlockPen) ? Win->DetailPen : menu_pen;	/* control if blockpen and detailpen are different */
			break;
		case TOK_CMDWIN:
			if (get_token())
				strcpy(CmdWindow, tok);
			break;
		case TOK_SHELLWIN:
			if (get_token())
				strcpy(ShellWindow, tok);
			break;
		case TOK_SHELLCMD:
			if (get_token())
				strcpy(ShellCmd, tok);
			break;
		case TOK_WAITCMD:
			if (get_token())
				strcpy(WaitCmd, tok);
			break;
		case TOK_TMPDIR:
			if (get_token())
				strcpy(TmpDir, tok);
			break;
		default:
			if (t)	/* allow empty lines */
				SYNTAX("Keyword expected")
		}
	}
	return TRUE;
}


BOOL ParseMenus(void)
{
	BPTR lock, cfg;
	struct FileInfoBlock *fib;
	long bufsize, nch;
	BOOL stat;
	BOOL pb = TRUE;

	if (!(lock = Lock(CurCfg, ACCESS_READ))) {
		SimpleRequest(ReqTitle,"Can't open '%s'",CurCfg);
		return FALSE;
	}
	if (fib = (struct FileInfoBlock *)
	    AllocMem(sizeof(struct FileInfoBlock), MEMF_PUBLIC|MEMF_CLEAR)) {
		if (Examine(lock, (BPTR)fib)) {
			if (fib->fib_DirEntryType < 0) {
				bufsize = fib->fib_Size + 2;
				if (FileBuffer = AllocMem(bufsize, MEMF_PUBLIC|MEMF_CLEAR)) {
					cfg = Open(CurCfg, MODE_OLDFILE);
					nch = Read(cfg, FileBuffer, bufsize-1);
					Close(cfg);
					if (nch == fib->fib_Size) {
						NextLine = FileBuffer;
						LineNum = 0;
						stat = ParseConfig();
						CleanUp();	/* setup items width */
						pb = FALSE;
					}
					FreeMem(FileBuffer, bufsize);
				}
			}
		}
		FreeMem(fib, sizeof(struct FileInfoBlock));
	}
	UnLock(lock);
	if (pb) {
		SimpleRequest(ReqTitle, "Error reading '%s'", CurCfg);
		return FALSE;
	}
	return stat;
}
