// Copyright (C) 1999-2005 Open Source Telecom Corporation.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// As a special exception, you may use this file as part of a free software
// library without restriction.  Specifically, if other files instantiate
// templates or use macros or inline functions from this file, or you compile
// this file and link it with other files to produce an executable, this
// file does not by itself cause the resulting executable to be covered by
// the GNU General Public License.  This exception does not however
// invalidate any other reasons why the executable file might be covered by
// the GNU General Public License.
//
// This exception applies only to the code released under the name GNU
// ccScript.  If you copy code from other releases into a copy of GNU
// ccScript, as the General Public License permits, the exception does
// not apply to the code that you add in this way.  To avoid misleading
// anyone as to the status of such modified files, you must delete
// this exception notice from them.
//
// If you write modifications of your own for GNU ccScript, it is your choice
// whether to permit this exception to apply to your modifications.
// If you do not wish that, delete this exception notice.
//

#include "engine.h"

using namespace std;
using namespace ost;

static long tens[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000};

bool ScriptMethods::scrNop(void)
{
	skip();
	return true;
}

bool ScriptMethods::scrIf(void)
{
	if(conditional())
	{
		if(frame[stack].index < frame[stack].line->argc)
			return intGoto();
		skip();
		if(frame[stack].line->scr.method == (Method)&ScriptMethods::scrThen)
			skip();
		return true;
	}
	skip();
	return true;
}

bool ScriptMethods::scrIfThen(void)
{
	if(!conditional())
		skip();

	skip();
	return true;
}

bool ScriptMethods::scrSlog(void)
{
	const char *member = getMember();
	const char *opt;

	if(member)
	{
		if(!stricmp(member, "debug"))
			slog(Slog::levelDebug);
		else if(!stricmp(member, "info"))
			slog(Slog::levelInfo);
		else if(!stricmp(member, "notice"))
			slog(Slog::levelNotice);
		else if(!strnicmp(member, "warn", 4))
			slog(Slog::levelWarning);
		else if(!strnicmp(member, "err", 3))
			slog(Slog::levelError);
		else if(!strnicmp(member, "crit", 4))
			slog(Slog::levelCritical);
		else if(!stricmp(member, "alert"))
			slog(Slog::levelAlert);
		else if(!strnicmp(member, "emerg", 5))
			slog(Slog::levelEmergency);
		else
			slog(Slog::levelNotice);
	}
	else
		slog(Slog::levelNotice);

	slog() << logname << ": ";
	while(NULL != (opt = getValue(NULL)))
		slog() << opt;
	slog() << endl;
	skip();
	return true;
}

bool ScriptMethods::scrThen(void)
{
	int level = 0;
	Line *line;

	skip();

	while(NULL != (line = frame[stack].line))
	{
		skip();

		if(line->scr.method == (Method)&ScriptMethods::scrThen)
			++level;
		else if(line->scr.method == (Method)&ScriptMethods::scrElse)
		{
			if(!level)
				return true;
		}
		else if(line->scr.method == (Method)&ScriptMethods::scrEndif)
		{
			if(!level)
				return true;
			--level;
		}
	}
	return true;
}

bool ScriptMethods::scrElse(void)
{
	int level = 0;
	Line *line;

	skip();

	while(NULL != (line = frame[stack].line))
	{
		skip();

		if(line->scr.method == (Method)&ScriptMethods::scrThen)
			++level;
		else if(line->scr.method == (Method)&ScriptMethods::scrEndif)
		{
			if(!level)
				return true;
		}
	}
	return true;
}

bool ScriptMethods::scrEndif(void)
{
	frame[stack].tranflag = false;
	skip();
	return true;
}

bool ScriptMethods::scrInit(void)
{
	const char *cp, *value;
	Line *line = getLine();
	unsigned idx = 0;
	ScriptSymbols *syms;
	Symbol *sym;

	syms = getLocal();

	while(idx < line->argc)
	{
		cp = line->args[idx++];
		if(*cp != '=')
			continue;

		value = getContent(line->args[idx++]);
		syms = getLocal();
		sym = syms->find(++cp, (unsigned short)strlen(value));
		if(!sym)
			continue;
		if(sym->type != symINITIAL)
			continue;
		setString(sym->data, sym->size + 1, value);
		sym->type = symCONST;
	}
	skip();
	return true;
}

static bool gotField(const char *cp, char pack, unsigned count, const char* match, unsigned len)
{
	while(count-- && cp)
	{
		cp = strchr(cp, pack);
		if(cp && *cp == pack)
			++cp;
	}
	if(!cp)
		cp = ",";

	if(strnicmp(cp, match, len))
		return false;

	if(cp[len] == pack || !cp[len])
		return true;

	return false;
}

bool ScriptMethods::scrRemove(void)
{
	Symbol *sym;
	Array *a;
	ScriptProperty *p;
	const char *opt, *key;
	char buffer[1024];
	char pack = getPackToken();
	unsigned idx, ndx;
	char *cp, *dp;
	Line *line = getLine();
	bool queue = false;
	unsigned offset = 0, len, count;

	opt = getKeyword("offset");
	if(!opt)
		opt = getKeyword("field");
	if(opt)
		offset = atoi(opt);

	opt = getKeyword("token");
	if(opt && *opt)
		pack = *opt;

	if(!stricmp(line->cmd, "last") || !stricmp(line->cmd, "first"))
		queue = true;

	opt = getOption(NULL);

	sym = mapSymbol(opt);
	if(!sym)
	{
		error("symbol-missing");
		return true;
	}

	buffer[0] = 0;
	while(NULL != (opt = getValue(NULL)))
		addString(buffer, sizeof(buffer), opt);
	key = opt = buffer;
	count = offset;
	while(key && count--)
	{
		key = strchr(key, pack);
		if(key && *key == pack)
			++key;
	}
	if(!key)
		key = ",";

	count = 0;
	while(key[count] && key[count] != pack)
		++count;

	a = (Array *)&sym->data;

	switch(sym->type)
	{
	case symFIFO:
	case symSTACK:
		if(a->head == a->tail)
			break;
		if(sym->type == symFIFO)
		{
			dp = sym->data + sizeof(Array) + a->head * (a->rec + 1);
			if(gotField(dp, pack, offset, key, count))
			{
				++a->head;
				if(a->head >= a->count)
					a->head = 0;
				break;
			}
		}
		if(sym->type == symSTACK)
		{
			dp = sym->data + sizeof(Array) + a->tail * (a->rec + 1);
			if(gotField(dp, pack, offset, key, count))
			{
				if(!a->tail)
					a->tail = a->count;
				--a->tail;
				break;
			}
		}
		idx = a->head;
		if(sym->type == symSTACK)
			if(++idx >= a->count)
				idx = 0;
		for(;;)
		{
			if(++idx >= a->count)
				idx = 0;

			if(idx == a->tail)
				break;

			dp = sym->data + sizeof(Array) + idx * (a->rec + 1);
			if(gotField(dp, pack, offset, key, count))
				break;
		}

		if(idx == a->tail)
			break;

		for(;;)
		{
			ndx = idx + 1;
			if(ndx >= a->count)
				ndx = 0;
			if(ndx == a->tail && sym->type == symFIFO)
				break;
			if(idx == a->tail && sym->type == symSTACK)
				break;

			strcpy(sym->data + sizeof(Array) + idx * (a->rec + 1),sym->data + sizeof(Array) + ndx * (a->rec + 1));

			++idx;
			if(idx >= a->count)
				break;
		}
		a->tail = idx;
		if(sym->type == symSTACK)
		{
			if(!a->tail)
				a->tail = a->count;
			--a->tail;
		}
		break;
	case symARRAY:
		if(queue)
		{
			error("invalid-queue-symbol");
			break;
		}
		idx = 0;
		if(!a->tail)
			break;

		len = (unsigned)strlen(opt);
		while(idx < a->tail)
		{
			dp = sym->data + sizeof(Array) + idx * (a->rec + 1);
			if(gotField(dp, pack, offset, key, count))
				break;
			++idx;
		}
		if(a->head > idx)
			--a->head;
		while(idx < (unsigned)(a->tail - 1))
		{
			strcpy(sym->data + sizeof(Array) + idx * (a->rec + 1),
				sym->data + sizeof(Array) + (idx + 1) * (a->rec + 1));
			++idx;
		}
		if(a->tail)
			--a->tail;
		break;
	case symPROPERTY:
		memcpy(&p, &sym->data, sizeof(p));
		if(p->token())
			pack = p->token();
	case symNORMAL:
	case symNUMBER:
		if(queue)
		{
			error("invalid-queue-symbol");
			break;
		}
		cp = (char *)extract(sym);
		len = (unsigned int)strlen(buffer);
		while(cp && *cp)
		{
			if(!strncmp(cp, opt, len))
				if(cp[len] == pack || !cp[len])
					break;

			cp = strchr(cp, pack);
			if(cp && *cp == pack)
				++cp;
		}
		if(!cp || !*cp)
			break;

		while(cp[len])
		{
			*cp = cp[len];
			++cp;
		}
		*cp = 0;
		break;
	default:
		error("symbol-invalid");
		return true;
	}

	if(!stricmp(line->cmd, "last"))
		append(sym, opt);

	if(!stricmp(line->cmd, "first") && sym->type == symFIFO)
	{
		idx = a->head;
		if(!idx)
			idx = a->count;
		if(--idx != a->tail)
		{
			a->head = idx;
			snprintf(sym->data + sizeof(Array) + a->head * (a->rec + 1), a->rec + 1, "%s", opt);
		}
	}

	if(!stricmp(line->cmd, "first") && sym->type == symSTACK)
	{
		idx = a->tail;
		if(++idx >= a->count)
			idx = 0;
		if(idx != a->tail)
		{
			a->tail = idx;
			snprintf(sym->data + sizeof(Array)+ a->tail * (a->rec + 1), a->rec + 1, "%s", opt);
		}
	}

	skip();
	return true;
}

bool ScriptMethods::scrArray(void)
{
	unsigned short rec = symsize - 10;
	unsigned char count;
	const char *kw = getKeyword("count");
	const char *mem = getMember();
	Symbol *sym;
	Array *a;
	unsigned size;
	char *err = NULL;
	Line *line = getLine();

	if(kw)
		count = atoi(kw);
	else
		count = atoi(mem);

	kw = getKeyword("size");
	if(kw)
		rec = atoi(kw);

	if(!count || !rec)
	{
		error("symbol-no-size");
		return true;
	}

	kw = line->cmd;
	if(!strnicmp(kw, "stack", 5) || !strnicmp(kw, "fifo", 4) || !strnicmp(kw, "lifo", 4))
		++count;

	size = (rec + 1) * count + sizeof(Array);

	while(NULL != (mem = getOption(NULL)))
	{
		sym = mapSymbol(mem, size);
		if(!sym)
		{
			err = "symbol-invalid";
			continue;
		}
		if(sym->type != symINITIAL || sym->size != size)
		{
			err = "symbol-already-defined";
			continue;
		}

		if(!strnicmp(kw, "array", 5))
			sym->type = symARRAY;
		else if(!strnicmp(kw, "fifo", 4))
			sym->type = symFIFO;
		else if(!strnicmp(kw, "stack", 5) || !strnicmp(kw, "lifo", 4))
			sym->type = symSTACK;

		a = (Array *)&sym->data;
		a->head = a->tail = 0;
		a->rec = rec;
		a->count = count;
	}
	skip();
	return true;
}

bool ScriptMethods::scrSet(void)
{
	unsigned max = symlimit;
	char buffer[1024];
	const char *id = getOption(NULL);
	const char *opt = getMember();
	ScriptProperty *prop = ScriptProperty::find(opt);
	unsigned short size = symsize;
	Symbol *sym;
	Line *line = getLine();
	bool cat = false;
	bool rtn;
	bool pset = false;
	unsigned ss = stack, len;
	int maxsize = 0, pos = 0;
	char *ep;

	if(opt && isdigit(*opt))
		size = atoi(opt);

	opt = getKeyword("size");
	if(opt)
	{
		maxsize = atoi(opt);
		size = atoi(opt);
	}

	opt = getKeyword("offset");
	if(opt)
		pos = atoi(opt);

	if(maxsize < 0)
	{
		pos += maxsize;
		maxsize = -maxsize;
		size = -maxsize;
	}

	if(max > 1024)
		max = 1024;

	if(!id || (*id != '%' && *id != '@' && *id != '&'))
	{
		error("set-invalid-symbol");
		return true;
	}

	buffer[0] = 0;
	while(NULL != (opt = getValue(NULL)))
		addString(buffer, max + 1, opt);

	if(!stricmp(line->cmd, "pset"))
		pset = true;

	opt = buffer;
	while(pset && stack)
	{
		--stack;
		if(frame[stack].local != frame[stack + 1].local)
			break;
	}
	sym = mapSymbol(id, size);
	stack = ss;
	if(!stricmp(line->cmd, "cat") || !stricmp(line->cmd, "add"))
		cat = true;
	else if(!strnicmp(line->cmd, "init", 4))
	{
		if(sym && sym->type != symINITIAL && sym->type != symORIGINAL)
		{
			skip();
			return true;
		}
	}

	if(!sym)
	{
		error("set-sym-missing");
		return true;
	}

	if(sym->type == symINITIAL && prop)
		sym->type = symPROPERTY;
	else if(sym->type == symINITIAL)
		sym->type = symNORMAL;

	len = (unsigned)strlen(opt);
	if(pos < 0 && -pos > (int)len)
		pos = 0;
	else if(pos < 0)
	{
		opt = opt + len + pos;
		len = -pos;
	}
	else if(pos >= (int)len)
		len = 0;
	else
	{
		opt += pos;
		len -= pos;
	}

	if(!len)
		opt = "";

	if(maxsize && (int)len > maxsize)
	{
		ep = (char *)opt + maxsize;
		*ep = 0;
	}

	if(cat)
		rtn = append(sym, opt);
	else
		rtn = commit(sym, opt);

	if(rtn)
		skip();
	else
		error("set-type-invalid");

	return true;
}

bool ScriptMethods::scrConst(void)
{
	unsigned max = symlimit;
	char buffer[1024], pbuf[1024];
	const char *id = getOption(NULL);
	const char *opt;
	ScriptProperty *prop = ScriptProperty::find(getMember());

	if(max > 1024)
		max = 1024;

	if(!id || (*id != '%' && *id != '@' && *id != '&'))
	{
		error("const-invalid-symbol");
		return true;
	}

	buffer[0] = 0;
	while(NULL != (opt = getValue(NULL)))
		addString(buffer, max + 1, opt);

	if(prop && !buffer[0])
	{
		prop->clear(pbuf);
		opt = buffer;
	}
	else if(prop)
	{
		prop->set(pbuf, buffer, max);
		opt = pbuf;
	}
	else
		opt = buffer;

	if(setConst(id, opt))
		skip();
	else
		error("const-already-defined");
	return true;
}

bool ScriptMethods::scrSequence(void)
{
	Line *line = getLine();
	const char *id = getOption(NULL);
	const char *opt;
	unsigned limit = line->argc;
	ScriptSymbols *syms;
	Symbol *sym;
	unsigned size = --limit * sizeof(opt);
	unsigned idx = 0;

	if(!id || (*id != '%' && *id != '@' && *id != '&'))
	{
		error("sequence-invalid-symbol");
		return true;
	}

	syms = getSymbols(id);
	if(!syms)
	{
		error("sequence-symbol-invalid");
		return true;
	}
	sym = syms->find(id, size);
	if(!sym)
	{
		error("sequence-symbol-invalid");
		return true;
	}
	if(sym->type != symINITIAL || sym->size != size)
	{
		error("sequence-already-defined");
		return true;
	}
	while(idx < limit)
	{
		opt = syms->cstring(getValue(""));
		memcpy(&sym->data[idx * sizeof(opt)], &opt, sizeof(opt));
		++idx;
	}
	sym->type = symSEQUENCE;
	sym->data[sym->size] = 0;
	skip();
	return true;
}


bool ScriptMethods::scrDecimal(void)
{
	frame[stack].decimal = atoi(getValue("0"));
	skip();
	return true;
}

bool ScriptMethods::scrError(void)
{
	char buffer[256];
	const char *cp;
	buffer[0] = 0;

	while(NULL != (cp = getValue(NULL)))
		addString(buffer, sizeof(buffer), cp);

	error(buffer);
	return true;
}

bool ScriptMethods::scrExit(void)
{
	while(stack)
		pull();

	frame[stack].line = NULL;
	return true;
}

bool ScriptMethods::scrSignal(void)
{
	const char *cp = getOption(NULL);

	if(!cp)
	{
		error("signal-target-missing");
		return true;
	}

	if(*cp == '^')
	{
		if(!signal(++cp))
			error("signal-trap-invalid");

		return true;
	}

	error("signal-target-invalid");
	return true;
}

bool ScriptMethods::scrThrow(void)
{
	const char *cp = getOption(NULL);

	if(!cp)
	{
		error("throw-target-missing");
		return true;
	}

	if(!scriptEvent(++cp))
		advance();
	return true;
}

bool ScriptMethods::scrGoto(void)
{
	unsigned argc = 0;
	Line *line = getLine();
	const char *var, *value;

	while(argc < line->argc)
	{
		var = line->args[argc++];
		if(*var != '=')
			continue;

		++var;
		value = getContent(line->args[argc++]);
		if(!value)
			continue;

		setSymbol(var, value, 0);
	}

	return intGoto();
}

bool ScriptMethods::intGoto(void)
{
	frame[stack].tranflag = false;
	if(image->isRipple() && frame[stack].local == NULL)
	{
		ripple();
		return true;
	}
	return redirect(true);
}

bool ScriptMethods::scrRestart(void)
{
	clearStack();
	branching();

	frame[stack].caseflag = frame[stack].tranflag = false;
	frame[stack].line = frame[stack].first = frame[stack].script->first;
	frame[stack].index = 0;
	if(isFunction(frame[stack].script))
		frame[stack].tranflag = true;

	return true;
}

bool ScriptMethods::scrTimer(void)
{
	const char *err = NULL;
	Symbol *sym;
	time_t now;
	const char *cp;
	const char *off = getKeyword("offset");
	const char *exp = getKeyword("expires");

	time(&now);

	while(NULL != (cp = getOption(NULL)))
	{
		sym = mapSymbol(cp, 23);
		if(!sym)
		{
err:
			err = "timer-symbol-invalid";
			continue;
		}
		if(sym->type == symINITIAL && sym->size > 22)
			goto set;
		if(sym->type == symTIMER)
			goto set;
		goto err;
set:
		snprintf(sym->data, 12, "%ld", now);
		sym->type = symTIMER;
		if(off)
			commit(sym, off);
		else if(exp && atol(exp) > 0)
			commit(sym, exp);
		else if(exp)
			sym->data[0] = 0;
	}
	if(err)
		error(err);
	else
		skip();
	return true;
}

bool ScriptMethods::scrCounter(void)
{
	unsigned long lval = 0;
	const char *err = NULL;
	const char *cp = getMember();
	Symbol *sym;

	if(cp)
		lval = atol(cp) - 1;

	while(NULL != (cp = getOption(NULL)))
	{
		sym = mapSymbol(cp, 11);
		if(!sym)
		{
			err = "var-symbol-invalid";
			continue;
		}
		if(sym->type != symINITIAL)
		{
			err = "var-already-defined";
			continue;
		}
		snprintf(sym->data, sym->size + 1, "%ld", lval);
		sym->type = symCOUNTER;
	}
	if(err)
		error(err);
	else
		skip();
	return true;
}

bool ScriptMethods::scrSession(void)
{
	const char *cp = getValue(NULL);
	ScriptInterp *interp;

	if(!cp)
	{
		session = this;
		skip();
		return true;
	}

	interp = getInterp(cp);
	if(interp)
	{
		session = interp;
		skip();
	}
	else
		error("session-invalid-id");

	return true;
}

bool ScriptMethods::scrLock(void)
{
	Symbol *sym;
	const char *cp = getOption(NULL);
	ScriptInterp *locker = NULL;
	unsigned long lockseq;
	char evtname[65];

	sym = mapSymbol(cp, 23);
	if(!sym)
	{
		error("lock-symbol-undefined");
		return true;
	}
	if(sym->type == symLOCK)
	{
		cp = strchr(sym->data, ':');
		if(cp)
		{
			locker = getInterp(++cp);
			lockseq = atol(sym->data);
		}
		if(locker && locker == this && lockseq == sequence)
			return true;
		if(locker && locker->getSequence() == lockseq)
		{
			snprintf(evtname, sizeof(evtname), "locked:%s", sym->id);
			if(scriptEvent(evtname))
				return true;
			error("lock-symbol-locked");
			return true;
		}
		sym->type = symINITIAL;
	}
	if(sym->size != 23 || sym->type != symINITIAL)
	{
		error("lock-symbol-invalid");
		return true;
	}
	snprintf(sym->data, sym->size + 1, "%ld:%u", sequence, getId());
	sym->type = symLOCK;
	skip();
	return true;
}

bool ScriptMethods::scrDeconstruct(void)
{
	const char *cp = getOption(NULL);
	const char *bp;
	char *dp;
	char name[65];
	char value[960];
	Symbol *sym = mapSymbol(cp, 0);
	Symbol *dest;

	if(!sym)
	{
		error("invalid-symbol");
		return true;
	}

	bp = sym->data;
	while(*bp)
	{
		dp = name;
		while(*bp && *bp != ':' && *bp != '=')
			*(dp++) = *(bp++);
		*dp = 0;
		if(!*bp)
			break;
		else
			++bp;
		dp = value;
		while(*bp && *bp != ';')
			*(dp++) = *(bp++);
		*dp = 0;
		if(*bp == ';')
			++bp;
		dest = getKeysymbol(name);
		if(!dest)
			continue;
		commit(dest, value);
	}
	skip();
	return true;
}

bool ScriptMethods::scrConstruct(void)
{
	const char *target = getOption(NULL);
	unsigned idx = 0;
	char buffer[1024];
	unsigned count = 0;
	const char *tag, *value;
	Line *line = getLine();
	char *ep;

	++target;
	while(idx < line->argc)
	{
		tag = line->args[idx++];
		if(!tag || *tag != '=')
			continue;

		++tag;
		value = getContent(line->args[idx++]);
		if(!value)
			continue;

		if(count)
			snprintf(buffer + count, sizeof(buffer) - count, ";%s=%s",
				tag, value);
		else
			snprintf(buffer + count, sizeof(buffer) - count, "%s=%s",
				tag, value);
		while(NULL != (ep = strchr(buffer + count + 1, ';')))
			*ep = ',';
		count = strlen(buffer);
	}
	setConst(target, buffer);
	advance();
	return true;
}

bool ScriptMethods::scrPack(void)
{
	char pack = getPackToken();
	const char *prefix = getKeyword("prefix");
	const char *suffix = getKeyword("suffix");
	const char *cp = getKeyword("token");
	const char *q = getKeyword("quote");
	Symbol *sym;
	unsigned size = symsize;
	char buffer[1024], tokbuf[2];
	ScriptProperty *p;
	bool unpack = false;
	Line *line = getLine();
	char *lp = buffer, *ep;
	unsigned offset = 0;

	if(!stricmp(line->cmd, "unpack"))
		unpack = true;

	if(q && !*q)
		q = NULL;

	if(cp && *cp)
		pack = *cp;

	cp = getKeyword("offset");
	if(!cp)
		cp = getKeyword("field");
	if(cp)
		offset = atoi(cp);

	cp = getKeyword("size");
	if(cp)
		size = atoi(cp);

	cp = getOption(NULL);
	if(unpack)
		sym = mapSymbol(cp);
	else
		sym = mapSymbol(cp, size);

	if(!sym)
	{
		error("symbol-invalid");
		return true;
	}

	if(sym->type == symPROPERTY)
	{
		memcpy(&p, &sym->data, sizeof(p));
		if(p->token())
			pack = p->token();
	}

	tokbuf[0] = 0;
	buffer[0] = 0;

	if(!stricmp(line->cmd, "unpack.struct"))
	{
		ep = sym->data;
		while(ep && *ep)
		{
			lp = strchr(ep, ':');
			if(!lp)
				lp = strchr(ep, '=');
			if(!lp)
				break;
			*(lp++) = 0;
			cp = (const char *)ep;
			ep = strchr(lp, ';');
			if(ep)
				*(ep++) = 0;
			setConst(cp, lp);
		}
		clear(sym);
		advance();
		return true;
	}

	if(!unpack && prefix)
		setString(buffer, sizeof(buffer), prefix);

	if(unpack)
	{
		cp = extract(sym);
		if(prefix && !strnicmp(cp, prefix, strlen(prefix)))
			cp += strlen(prefix);
		if(cp && *cp)
			setString(buffer, sizeof(buffer), cp);
		else
		{
			skip();
			return true;
		}
		if(suffix && !stricmp(buffer + strlen(buffer) - strlen(suffix), suffix))
			buffer[strlen(buffer) - strlen(suffix)] = 0;

		if(!q)
		{
			if(*lp == '\'')
				q = "'";
			else if(*lp == '"')
				q = "\"";
		}
	}

	while(unpack && NULL != (cp = getOption(NULL)))
	{
		if(offset)
		{
			sym = NULL;
			--offset;
		}
		else
		{
			sym = mapSymbol(cp, size);
			if(!sym)
			{
				error("symbol-missing");
				return true;
			}
		}

		if(*lp == pack)
		{
			commit(sym, "");
			++lp;
			continue;
		}

		if(q && !strnicmp(lp, q, strlen(q)))
		{
			lp += strlen(q);
			ep = strstr(lp, q);
			if(!ep)
				ep = lp + strlen(lp);
			else
			{
				*ep = 0;
				ep += strlen(q);
			}
			commit(sym, lp);
			if(*ep == pack)
				++ep;
			if(!*ep)
				break;
			lp = ep;
			continue;
		}
		ep = strchr(lp, pack);
		if(ep)
			*(ep++) = 0;
		if(sym)
			commit(sym, lp);
		if(!ep || !*ep)
			break;
		lp = ep;
	}

	while(!unpack)
	{
		if(offset)
		{
			cp = "";
			--offset;
		}
		else
			cp = getOption(NULL);

		if(!cp)
			break;
		if(tokbuf[0])
			addString(buffer, sizeof(buffer), tokbuf);
		tokbuf[0] = pack;
		tokbuf[1] = 0;
		if(!stricmp(line->cmd, "pack.struct"))
		{
			tokbuf[0] = ';';
			addString(buffer, sizeof(buffer), cp + 1);
			addString(buffer, sizeof(buffer), ":");
			lp = buffer + strlen(buffer);
		}
		else
			lp = NULL;
		if(q)
			addString(buffer, sizeof(buffer), q);
		cp = getContent(cp);
		if(cp)
			addString(buffer, sizeof(buffer), cp);
		while(lp && *lp)
		{
			lp = strchr(lp, ';');
			if(lp)
				*(lp++) = ',';
		}
		if(q)
			addString(buffer, sizeof(buffer), q);
	}

	if(!unpack)
	{
		if(suffix)
			addString(buffer, sizeof(buffer), suffix);

		if(!commit(sym, buffer))
		{
			error("symbol-not-packable");
			return true;
		}
	}

	skip();
	return true;
}

bool ScriptMethods::scrClear(void)
{
	const char *cp;
	Symbol *sym;
	const char *err = NULL;
	ScriptInterp *locker;
	unsigned long lockseq;

	while(NULL != (cp = getOption(NULL)))
	{
		sym = mapSymbol(cp, 0);
		if(!sym)
		{
			err = "clear-symbol-undefined";
			continue;
		}
		if(sym->type == symLOCK)
		{
			locker = NULL;
			cp = strchr(sym->data, ':');
			if(cp)
				locker = getInterp(++cp);
			lockseq = atol(sym->data);
			if(locker && locker != this && lockseq == locker->getSequence())
			{
				err = "clear-symbol-locked";
				continue;
			}
		}
		clear(sym);
	}
	if(err)
		error(err);
	else
		skip();
	return true;
}

bool ScriptMethods::scrType(void)
{
	Symbol *tsym, temp, *sym;
	const char *cp = getOption(NULL);

	tsym = mapSymbol(cp, 0);
	if(!tsym)
	{
		error("symtype-missing");
		return true;
	}

	while(NULL != (cp = getOption(NULL)))
	{
		sym = mapSymbol(cp, tsym->size);
		if(!sym)
			continue;

		if(sym->type != symINITIAL && sym->type != tsym->type)
			continue;

		if(sym->size != tsym->size)
			continue;

		temp.id = sym->id;
		temp.next = sym->next;

		memcpy(sym, tsym, sizeof(Symbol) + tsym->size);
		sym->id = temp.id;
		sym->next = temp.next;
	}

	advance();
	return true;
}

bool ScriptMethods::scrDefine(void)
{
	char var[128];
	char base[65];
	Symbol *sym;
	const char *cp, *pv;
	const char *prefix = getMember();
	unsigned len;
	char *ep;
	Line *line = getLine();
	Name *scr = getName();
	unsigned idx = 0, vlen;

	if(!prefix && !frame[stack].local)
	{
		setString(base, sizeof(base), scr->name);
		ep = strchr(base, ':');
		if(ep)
			*ep = 0;
		prefix = base;
	}

	while(idx < line->argc)
	{
		pv = NULL;
		cp = line->args[idx++];
		if(*cp == '=')
		{
			pv = line->args[idx++];
			++cp;
			if(*pv == '{')
				++pv;
		}

		if(prefix && !strchr(cp, '.') && *cp != '%' && *cp != '&')
	                snprintf(var, sizeof(var), "%s.%s", prefix, cp);
		else
			setString(var, sizeof(var), cp);

		ep = strrchr(var, ':');
		if(ep)
		{
			*(ep++) = 0;
			len = atoi(ep);
		}
		else if(pv)
			len = 0;
		else
			len = symsize;

		if(!pv)
			pv = "";

		if(!len)
			vlen = (unsigned)strlen(pv);
		else
			vlen = len;

		if(!vlen)
			++vlen;

		sym = mapSymbol(var, vlen);
		if(!sym || sym->type != symINITIAL)
			continue;

		setString(sym->data, sym->size + 1, pv);
		if(len)
			sym->type = symNORMAL;
		else
			sym->type = symCONST;

	}
	advance();
	return true;
}

bool ScriptMethods::scrVar(void)
{
	unsigned short size = symsize;
	const char *cp = getMember();
	Symbol *sym;
	char *errmsg = NULL;
	Line *line = getLine();
	const char *value = getKeyword("value");

	if(!stricmp(line->cmd, "char") || !stricmp(line->cmd, "bool"))
		size = 1;

	if(cp)
		size = atoi(cp);

	cp = getKeyword("size");
	if(cp)
		size = atoi(cp);

	while(NULL != (cp = getOption(NULL)))
	{
		sym = mapSymbol(cp, size);
		if(!sym)
		{
			errmsg = "var-symbol-invalid";
			continue;
		}

		if(sym->type != symINITIAL || sym->size != size)
		{
			errmsg = "var-already-defined";
			continue;
		}
		if(!stricmp(line->cmd, "bool"))
		{
			sym->type = symBOOL;
			if(!value)
				commit(sym, "false");
		}
		if(value)
			commit(sym, value);
	}

	if(errmsg)
		error(errmsg);
	else
		skip();

	return true;
}

bool ScriptMethods::scrExpr(void)
{
	unsigned prec = 0;
	Symbol *sym;
	const char *mem = getMember();
	const char *opt;
	ScriptProperty *prop = NULL;
	long iNumber, hNumber, lNumber;
	char result[20];
	char presult[65];
	char fmt[13];
	unsigned len;
	Array *a;

	if(mem)
	{
		prop = ScriptProperty::find(mem);
		prec = atoi(mem);
		if(prop)
			prec = prop->prec();
	}

	while(NULL != (opt = getOption(NULL)))
	{
		if(!stricmp(opt, "-eq"))
			break;

		if(*opt == '%' || *opt == '&' || *opt == '@')
		{
			sym = mapSymbol(opt, 0);
			if(!sym)
			{
				error("expr-sym-undefined");
				return true;
			}
			if(sym->type == symNUMBER && !prec && !prop && sym->size > 11)
				prec = sym->size - 12;
			continue;
		}

		error("expr-invalid");
		return true;
	}
	if(!opt)
	{
		error("expr-missing");
		return true;
	}

	if(!prec)
		prec = frame[stack].decimal;

	if(numericExpression(&iNumber, 1, prec, prop) != 1)
	{
		error("expr-invalid");
		return true;
	}

	snprintf(fmt, sizeof(fmt), "%s%d%s", "%ld.%0", prec, "ld");
	frame[stack].index = 0;
	hNumber = iNumber / tens[prec];
	lNumber = iNumber % tens[prec];
	if(lNumber < 0)
		lNumber = -lNumber;
	if(prec)
		snprintf(result, sizeof(result), fmt, hNumber, lNumber);
	else
		snprintf(result, sizeof(result), "%ld", iNumber);

	while(NULL != (opt = getOption(NULL)))
	{
		if(!stricmp(opt, "-eq"))
			break;

		sym = mapSymbol(opt, 0);

		if(!sym)
			continue;

		if(sym->type == symNUMBER || !prop)
		{
			if(!commit(sym, result))
			{
				error("expr-cannot-assign");
				return true;
			}
			continue;
		}

		a = (Array *)&sym->data;
		switch(sym->type)
		{
		case symINITIAL:
			sym->type = symNORMAL;
		case symNORMAL:
		case symPROPERTY:
			prop->setValue(sym->data, sym->size, iNumber);
			break;
		case symARRAY:
		case symSTACK:
		case symFIFO:
			len = a->rec;
			if(len >= sizeof(presult))
				len = sizeof(presult) - 1;
			prop->setValue(presult, len, iNumber);
			commit(sym, presult);
		default:
			error("expr-cannot-assign");
			return true;
		}
	}
	skip();
	return true;
}

bool ScriptMethods::scrIndex(void)
{
	Symbol *sym;
	const char *opt;
	long iNumber;

	while(NULL != (opt = getOption(NULL)))
	{
		if(!stricmp(opt, "-eq"))
			break;

		if(*opt == '%' || *opt == '&' || *opt == '@')
		{
			sym = mapSymbol(opt, 0);
			if(!sym)
			{
				error("index-sym-undefined");
				return true;
			}
			switch(sym->type)
			{
			case symARRAY:
			case symFIFO:
			case symSTACK:
				break;
			default:
				error("index-not-array");
				return true;
			}
			continue;
		}

		error("index-invalid");
		return true;
	}
	if(!opt)
	{
		error("index-expr-missing");
		return true;
	}

	if(numericExpression(&iNumber, 1, 0, NULL) != 1)
	{
		error("index-invalid");
		return true;
	}

	while(NULL != (opt = getOption(NULL)))
	{
		if(!stricmp(opt, "-eq"))
			break;

		sym = mapSymbol(opt, 0);

		if(!sym)
			continue;

		if(iNumber > 0)
			--iNumber;

		symindex(sym, (short)iNumber);
	}
	skip();
	return true;
}

bool ScriptMethods::scrNumber(void)
{
	unsigned short size = 11;
	const char *cp = getMember();
	Symbol *sym;
	char *errmsg = NULL;
	Line *line = getLine();

	if(!strnicmp(line->cmd, "num", 3) && frame[stack].decimal)
		size = 12 + frame[stack].decimal;

	if(cp)
		size = 12 + atoi(cp);

	cp = getKeyword("decimal");
	if(cp)
		size = 12 + atoi(cp);

	while(NULL != (cp = getOption(NULL)))
	{
		sym = mapSymbol(cp, size);
		if(!sym)
		{
			errmsg = "var-symbol-invalid";
			continue;
		}

		if(sym->type != symINITIAL || sym->size != size)
		{
			errmsg = "var-already-defined";
			continue;
		}

		sym->type = symNUMBER;
		clear(sym);
	}

	if(errmsg)
		error(errmsg);
	else
		skip();

	return true;
}


bool ScriptMethods::scrRef(void)
{
	const char *target = getOption(NULL);
	Symbol *sym = getSymbol();
	ScriptSymbols *syms;

	if(!target)
	{
		error("ref-target-unknown");
		return true;
	}

	if(!sym)
	{
		error("ref-missing-source");
		return true;
	}

	syms = getSymbols(sym->id);
	if(syms != dynamic_cast<ScriptSymbols*>(this) && syms != frame[stack].local)
	{
		error("ref-invalid-source");
		return true;
	}

	if(strchr(target, '.'))
	{
		error("ref-invalid-target");
		return true;
	}

	if(frame[stack].local)
		syms = frame[stack].local;
	else
		syms = dynamic_cast<ScriptSymbols*>(this);

	if(!syms->setReference(target, sym))
		error("ref-failed");
	else
		skip();

	return true;
}

bool ScriptMethods::scrReturn(void)
{
	Line *line = getLine();
	const char *label = getOption(NULL), *var;
	char *ext;
	char namebuf[256];
	unsigned len = symsize;
	unsigned argc = 0;
	Name *scr;
	char *n;

	if(label && *label != '@' && *label != '{')
		label = getContent(label);

	tempidx = 0;
	while(argc < line->argc)
	{
		if(*line->args[argc++] != '=')
			continue;

		snprintf(temps[tempidx], symsize + 1,
			"%s", getContent(line->args[argc]));

//		line->args[argc] = temps[tempidx];
		if(tempidx++ >= SCRIPT_TEMP_SPACE)
			tempidx = 0;
		++argc;
	}

	while(stack)
	{
		if(frame[stack - 1].local == frame[stack].local &&
			frame[stack - 1].script == frame[stack].script)
			pull();
		else
			break;
	}

	if(stack)
		pull();
	else
	{
		error("return-failed");
		return true;
	}

	argc = 0;
	tempidx = 0;
	while(argc < line->argc)
	{
		var = line->args[argc++];
		if(*var != '=')
			continue;

		++argc;
		if(*(++var) == '%')
			++var;
		else if(*var == '.')
		{
			snprintf(namebuf, sizeof(namebuf), "%s", frame[stack].script->name);
			n = strchr(namebuf, ':');
			if(n)
				*n = 0;
			snprintf(namebuf + strlen(namebuf), sizeof(namebuf) - strlen(namebuf), "%s", var);
			var = namebuf;
		}
		ext = temps[tempidx++];
		if(tempidx >= SCRIPT_TEMP_SPACE)
			tempidx = 0;
		setSymbol(var, ext, len);
	}

retry:
	if(!label)
	{
		skip();
		return true;
	}

	if(!*label)
	{
		skip();
		return true;
	}
	if(*label == '@' || *label == '{')
	{
		if(scriptEvent(label + 1))
			return true;
	}
	if(*label == '^')
	{
		if(!signal(++label))
		{
			error("trap-invalid");
			return true;
		}
		return true;
	}
	len = (unsigned)strlen(label);
	if(!strncmp(label, "::", 2))
	{
		setString(namebuf, sizeof(namebuf), frame[stack].script->name);
		ext = strstr(namebuf, "::");
		if(ext)
			setString(ext, sizeof(namebuf) + namebuf - ext, label);
		else
			addString(namebuf, sizeof(namebuf), label);
		label = namebuf;
	}
	else if(label[len - 1] == ':')
	{
		setString(namebuf, sizeof(namebuf), frame[stack].script->name);
		ext = strstr(namebuf, "::");
		if(ext)
			*ext = 0;

		addString(namebuf, sizeof(namebuf), "::");
		addString(namebuf, sizeof(namebuf), label);

		label = ext = namebuf;
		len = (unsigned)strlen(label);
		ext[len - 1] = 0;
	}

	scr = getScript(label);
	if(!scr)
	{
		label = getValue(NULL);
		if(label)
			goto retry;
		error("script-not-found");
		return true;
	}
	clearStack();
	frame[stack].caseflag = frame[stack].tranflag = false;
	frame[stack].script = scr;
	frame[stack].line = frame[stack].first = scr->first;
	frame[stack].index = 0;
	frame[stack].mask = getMask();
	return true;
}

bool ScriptMethods::scrCall(void)
{
	Line *line = getLine();
	const char *cmd = line->cmd;
	const char *cp, *vp;
	unsigned idx = 0;
	Symbol *sym;
	unsigned argc = 0;
	char argn[8];

	if(!push())
	{
		error("stack-overflow");
		return true;
	}

	frame[stack].tranflag = false;
	frame[stack].index = 0;
	frame[stack].caseflag = false;

	if(!strnicmp(cmd, "source", 6))
	{
		frame[stack].tranflag = true;
		return redirect(false);
	}

	frame[stack].local = new ScriptSymbols();

	if(!strnicmp(cmd, "call", 4))
		frame[stack].tranflag = true;
	else if(!strnicmp(cmd, "gosub", 5))
		frame[stack].base = stack;

	while(idx < line->argc)
	{
		cp = line->args[idx++];
		if(*cp != '=')
		{
			if(!argc)
			{
				++argc;
				continue;
			}
			snprintf(argn, sizeof(argn), "%d", argc++);
			if(*cp == '&')
			{
				vp = cp;
				cp = argn;
				goto byref;
			}
			--stack;
			cp = getContent(cp);
			++stack;
			setConst(argn, cp);
			continue;
		}

		++cp;
		vp = line->args[idx++];
		if(*vp == '&')
		{
byref:
			--stack;
			sym = mapSymbol(vp);
			++stack;
 			frame[stack].local->setReference(cp, sym);
			continue;
		}
		--stack;
		vp = getKeyword(cp);
		++stack;
		setConst(cp, vp);
	}
	snprintf(argn, sizeof(argn), "%d", --argc);
	setConst("_", argn);
	return redirect(false);
}

bool ScriptMethods::scrBegin(void)
{
	if(frame[stack].tranflag)
	{
		error("begin-already-in-transaction");
		return true;
	}

	frame[stack].tranflag = true;
	skip();
	return true;
}

bool ScriptMethods::scrEnd(void)
{
	if(!frame[stack].tranflag)
	{
		error("end-not-in-transaction");
		return true;
	}
	frame[stack].tranflag = false;
	skip();
	return true;
}

bool ScriptMethods::scrCase(void)
{
	unsigned short loop = 0xfffe;
	Line	*line;

	if(!frame[stack].caseflag)
		if(conditional() || !frame[stack].line->argc)
		{
			frame[stack].caseflag = true;
			skip();
			while(frame[stack].line)
			{
				if(frame[stack].line->scr.method == (Method)&ScriptMethods::scrCase)
					skip();
				else
					return true;
			}
			return true;
		}

	if(stack && frame[stack].line->loop)
		loop = frame[stack - 1].line->loop;

	skip();
	while(NULL != (line = frame[stack].line))
	{
		if(line->loop == loop)
			return true;

		if(line->scr.method == (Method)&ScriptMethods::scrCase && !frame[stack].caseflag)
			return true;

		if(line->scr.method == (Method)&ScriptMethods::scrEndcase)
			return true;

		skip();
	}
	return true;
}

bool ScriptMethods::scrEndcase(void)
{
	frame[stack].caseflag = frame[stack].tranflag = false;
	skip();
	return true;
}

bool ScriptMethods::scrOffset(void)
{
	long offset;
	Method method;
	Line *line;

	numericExpression(&offset, 1, 0);
	--offset;
	if(!stack)
	{
		error("stack-underflow");
		return true;
	}

	line = frame[stack - 1].line;
	method = line->scr.method;

	if(method != (Method)&ScriptMethods::scrForeach &&
	   method != (Method)&ScriptMethods::scrFor)
	{
		error("offset-not-indexed-loop");
		return true;
	}
	--stack;
	if(offset < 0)
	{
		if((unsigned)(-offset) >= frame[stack].index)
			frame[stack].index = 1;
		else
			frame[stack].index += (unsigned short)offset;
	}
	else
		frame[stack].index += (unsigned short)offset;
	updated = false;
	return execute(frame[stack].line->scr.method);
}

bool ScriptMethods::scrRepeat(void)
{
	unsigned short loop = frame[stack].line->loop;
	Line *line;
	int index = frame[stack].index;
	long count;

	frame[stack].index = 0;
	numericExpression(&count, 1, 0);

	if(index >= count)
	{
		line = frame[stack].line->next;
		while(line)
		{
			if(line->loop == loop)
			{
				frame[stack].line = line;
				skip();
				return true;
			}
			line = line->next;
		}
		error("loop-overflow");
		return true;
	}

	frame[stack].index = ++index;
	if(!push())
		return true;

	skip();
	return true;
}

bool ScriptMethods::scrFor(void)
{
	Symbol *sym;
	unsigned short loop = frame[stack].line->loop;
	Line *line;
	int index = frame[stack].index;
	unsigned size = symsize;
	const char *opt;
	const char *kw = getKeyword("size");
	char buffer[12];

	if(kw)
		size = atoi(kw);

	frame[stack].index = 0;
	opt = getOption(NULL);

	if(!index)
	{
		kw = getKeyword("index");
		if(kw)
			index = atoi(kw);
	}

	if(!index)
		++index;

	sym = getKeysymbol("index");
	if(sym)
	{
		snprintf(buffer, sizeof(buffer), "%d", index);
		commit(sym, buffer);
	}

	sym = mapSymbol(opt, size);

	if(!sym)
	{
		error("symbol-not-found");
		return true;
	}

	frame[stack].index = index;
	opt = getValue(NULL);

	if(!opt)
	{
failed:
		line = frame[stack].line->next;
		while(line)
		{
			if(line->loop == loop)
			{
				frame[stack].line = line;
				skip();
				return true;
			}
			line = line->next;
		}
		error("loop-overflow");
		return true;
	}

	if(!push())
		goto failed;

	if(commit(sym, opt))
		skip();
	else
		error("for-cannot-set");

	return true;
}

bool ScriptMethods::scrForeach(void)
{
	Symbol *sym, *src;
	unsigned short loop = frame[stack].line->loop;
	Line *line;
	int index = frame[stack].index;
	unsigned size = symsize;
	const char *opt, *val;
	char pack = getPackToken();
	const char *kw = getKeyword("size");
	char buffer[1024];
	char *cp;
	Array *a;
	ScriptProperty *p;

	if(kw)
		size = atoi(kw);

	kw = getKeyword("token");
	if(kw && *kw)
		pack = *kw;

	frame[stack].index = 0;
	opt = getOption(NULL);
	val = getOption(NULL);

	if(!index)
	{
		kw = getKeyoption("index");
		if(kw)
			kw = getSymContent(kw);
		if(kw)
			index = atoi(kw);
	}

	if(!index)
		++index;

	sym = getKeysymbol("index");
	if(sym)
	{
		snprintf(buffer, 11, "%d", index);
		commit(sym, buffer);
	}

	sym = mapSymbol(opt, size);
	src = mapSymbol(val, 0);

	if(!sym || !src)
	{
		error("symbol-not-found");
		return true;
	}

	frame[stack].index = index;
	++frame[stack].index;

	opt = NULL;
	switch(src->type)
	{
	case symARRAY:
		if(!symindex(src, --index))
			goto failed;
	case symFIFO:
	case symSTACK:
		a = (Array *)&src->data;
		if(a->head == a->tail)
			goto failed;
		opt = extract(src);
	default:
		break;
	case symPROPERTY:
		memcpy(&p, &sym->data, sizeof(p));
		if(p->token())
			pack = p->token();
	case symNORMAL:
	case symNUMBER:
		opt = extract(src);
		while(opt && --index)
		{
			opt = strchr(opt, pack);
			if(opt && *opt == pack)
				++opt;
		}
		if(opt)
		{
			snprintf(buffer, sizeof(buffer), "%s", opt);
			opt = buffer;
			cp = (char *)strchr(opt, pack);
			if(cp)
				*cp = 0;
		}
	}

	if(!opt)
	{
failed:
		line = frame[stack].line->next;
		while(line)
		{
			if(line->loop == loop)
			{
				frame[stack].line = line;
				skip();
				return true;
			}
			line = line->next;
		}
		error("loop-overflow");
		return true;
	}

	if(!push())
		goto failed;

	if(commit(sym, opt))
		skip();
	else
		error("for-cannot-set");

	return true;
}

bool ScriptMethods::scrDo(void)
{
	unsigned short loop = frame[stack].line->loop;
	Line *line;

	frame[stack].index = 0;	// always reset

	if(frame[stack].line->argc)
	{
		if(!conditional())
		{
			line = frame[stack].line->next;
			while(line)
			{
				if(line->loop == loop)
				{
					frame[stack].line = line;
					skip();
					return true;
				}
				line = line->next;
			}
			error("loop-overflow");
			return true;
		}
	}

	if(!push())
		return true;

	skip();
	return true;
}

bool ScriptMethods::scrLoop(void)
{
	unsigned short loop;

	if(stack < 1)
	{
		error("stack-underflow");
		return true;
	}

	loop = frame[stack - 1].line->loop;
	if(!loop)
	{
		error("stack-not-loop");
		return true;
	}

	if(frame[stack].line->argc)
	{
		if(!conditional())
		{
			frame[stack - 1] = frame[stack];
			--stack;
			skip();
			return true;
		}
	}

	--stack;
	updated = false;
	return execute(frame[stack].line->scr.method);
}

bool ScriptMethods::scrContinue(void)
{
	Line *line;
	unsigned short loop;

	if(frame[stack].line->argc)
	{
		if(!conditional())
		{
			skip();
			return true;
		}
	}

	if(stack < 1)
	{
		error("stack-underflow");
		return true;
	}

	loop = frame[stack - 1].line->loop;
	line = frame[stack].line->next;

	if(!loop)
	{
		error("stack-not-loop");
		return true;
	}

	while(line)
	{
		if(line->loop == loop)
		{
			frame[stack].line = line;
			return true;
		}
		line = line->next;
	}
	error("loop-overflow");
	return true;
}

bool ScriptMethods::scrBreak(void)
{
	Line *line;
	unsigned short loop;

	if(frame[stack].line->argc)
	{
		if(!conditional())
		{
			skip();
			return true;
		}
	}

	if(stack < 1)
	{
		error("stack-underflow");
		return true;
	}

	loop = frame[stack - 1].line->loop;
	line = frame[stack].line->next;

	if(!loop)
	{
		error("stack-not-loop");
		return true;
	}

	while(line)
	{
		if(line->loop == loop)
		{
			--stack;
			frame[stack].line = line;
			skip();
			return true;
		}
		line = line->next;
	}
	error("loop-overflow");
	return true;
}


syntax highlighted by Code2HTML, v. 0.9.1