// 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;

ScriptCompiler::ScriptCompiler(ScriptCommand *cmd, const char *symset) :
ScriptImage(cmd, symset)
{
	inccount = 0;
	mlist = NULL;
	scrStream = (istream *)&scrSource;
}

void ScriptCompiler::fastBranch(ScriptInterp *interp)
{
	Line *line;
	ScriptInterp::Frame *frame;
	Method m;
	unsigned maxstep = fastStepping;
	bool nongoto = false;

	frame = interp->getFrame();
	if(frame->line == frame->script->first)
		nongoto = true;

	while(maxstep-- && NULL != (line = interp->getLine()))
	{
		m = line->scr.method;
		if(line->loop == 0xffff)
		{
			if(interp->getTrace())
				return;
			interp->setFrame();
			interp->execute(m);
		}
		else if(m == (Method)&ScriptMethods::scrBegin ||
			m == (Method)&ScriptMethods::scrReturn)
		{
			interp->setFrame();
			interp->execute(m);
			return;
		}
		else if(m == (Method)&ScriptMethods::scrGoto ||
			m == (Method)&ScriptMethods::scrRestart)
		{
			if(nongoto)
				return;
			interp->setFrame();
			interp->execute(m);
			return;
		}
		else
			return;
	}
}

bool ScriptCompiler::checkSegment(Name *scr)
{
	return true;
}

const char *ScriptCompiler::preproc(const char *token)
{
	return "unknown keyword";
}

const char *ScriptCompiler::getDefined(const char *token)
{
	return getLast(token);
}

void ScriptCompiler::commit(void)
{
	Name *scr, *target;
	const char *last = "";
	NamedEvent *ev, *from;
	unsigned ecount;
	const char *en;
	char pbuf[65];
	char *ep;

	while(inccount)
		include(incfiles[--inccount]);

	while(mlist)
	{
		scr = getScript(mlist->source);
		if(!scr)
		{
			if(mlist->source != last)
				slog.error("include from %s not found", mlist->source);
			goto cont;
		}
		ecount = 0;
		en = mlist->prefix;
		if(!*en)
			en = "*:";

		target = mlist->target;
		from = scr->events;
		while(from)
		{
			if(!strnicmp(from->name, mlist->prefix, strlen(mlist->prefix)))
			{
				ev = (NamedEvent *)alloc(sizeof(NamedEvent));
				ev->line = from->line;
				ev->name = from->name;
				ev->type = from->type;
				ev->next = target->events;
				target->events = ev;
				++ecount;
			}

			from = from->next;
		}
		if(ecount)
		{
			setString(pbuf, sizeof(pbuf), en);
			ep = strchr(pbuf, ':');
			if(ep)
				*ep = 0;
			slog.debug("included %s from %s; %d events", pbuf, mlist->source, ecount);
		}
cont:
		last = mlist->source;
		mlist = mlist->next;
	}

	ScriptImage::commit();
}

char *ScriptCompiler::getToken(char **pre)
{
	static char temp[513];
	char *cp = temp + 1;
	char *base = temp + 1;
	char q;
	int level;

	if(pre)
		if(*pre)
		{
			cp = *pre;
			*pre = NULL;
			return cp;
		}

	if(*bp == '=')
	{
		++bp;
	   		if(*bp == '{')
			{
					level = -1;
					while(*bp)
					{
				if(*bp == '}' && !level)
					break;
				if(*bp == '{')
					++level;
				else if(*bp == '}')
					--level;
				*(cp++) = *(bp++);
					}
					if(*bp == '}')
				++bp;
					*cp = 0;
			*base = 0x01;	// special token
					return base;
			}

		if(*bp == ' ' || *bp == '\t' || !*bp)
			return "";
		if(*bp == '\"' || *bp == '\'')
		{
			q = *(bp++);
			while(q)
			{
				switch(*bp)
				{
				case '\\':
					++bp;
					if(!*bp)
					{
						q = 0;
						break;
					}
					switch(*bp)
					{
					case 't':
						*(cp++) = '\t';
						++bp;
						break;
					case 'b':
						*(cp++) = '\b';
						++bp;
						break;
					case 'n':
						*(cp++) = '\n';
						++bp;
						break;
					default:
						*(cp++) = *(bp++);
					}
					break;
				case 0:
					q = 0;
					break;
				default:
					if(*bp == q)
					{
						++bp;
						q = 0;
					}
					else
						*(cp++) = *(bp++);
				}
			}
			*cp = 0;
			return base;
		}
		while(*bp != ' ' && *bp != '\t' && *bp)
			*(cp++) = *(bp++);
		*cp = 0;
		return base;
	}

	if(!quote)
		while(*bp == ' ' || *bp == '\t')
			++bp;

	if(!quote && *bp == '#' && (!bp[1] || bp[1] == '!' || isspace(bp[1])))
		return NULL;

	if(!quote && bp[0] == '%' && bp[1] == '%' && (!bp[2] || isspace(bp[2])))
		return NULL;


	if(!*bp)
	{
		paren = 0;
		quote = false;
		return NULL;
	}

	if(*bp == '\"' && !quote)
	{
		++bp;
		quote = true;
	}

	if(!quote && *bp == '{')
	{
		level = -1;
		while(*bp)
		{
			if(*bp == '}' && !level)
				break;
			if(*bp == '{')
				++level;
			else if(*bp == '}')
				--level;
			*(cp++) = *(bp++);
		}
		if(*bp == '}')
			++bp;
		*cp = 0;
		return base;
	}

	if(!quote)
	{
		if(*bp == ',' && paren)
		{
			++bp;
			return ",";
		}
retry:
		while(*bp && !isspace(*bp) && *bp != ',' && *bp != '=' && *bp != '(' && *bp != ')' )
			*(cp++) = *(bp++);

		if(*bp == '(')
			++paren;
		else if(*bp == ')')
			--paren;

		if(*bp == '=' && cp == base)
		{
			*(cp++) = *(bp++);
			goto retry;
		}

		if(*bp == '=' && cp == base + 1 && ispunct(*base))
		{
			*(cp++) = *(bp++);
			goto retry;
		}

		if((*bp == '(' || *bp == ')') && cp == base)
			*(cp++) = *(bp++);

		*cp = 0;
		if(*bp == ',' && !paren)
			++bp;
		else if(*bp == '=')
			*(--base) = *(bp);
		if(!strcmp(base, "=="))
			return ".eq.";
		if(!strcmp(base, "="))
			return "-eq";
		if(!strncmp(base, "!=", 2))
			return ".ne.";
		if(!strncmp(base, "<>", 2))
			return "-ne";
		if(!strcmp(base, "<"))
			return "-lt";
		if(!strcmp(base, "<="))
			return "-le";
		if(!strcmp(base, ">"))
			return "-gt";
		if(!strcmp(base, ">="))
			return "-ge";
		return base;
	}

	if(*bp == '\\' && (bp[1] == '%' || bp[1] == '&' || bp[1] == '.' || bp[1] == '#'))
	{
		++bp;
		*(cp++) = '{';
		*(cp++) = *(bp++);
	}

requote:
	if(isalnum(*bp) || strchr("~/:,. \t\'", *bp))
	{
		while(isalnum(*bp) || strchr("~=/:,. \t\'", *bp))
			*(cp++) = *(bp++);
	}
	else while(!isspace(*bp) && *bp && *bp != '\"')
		*(cp++) = *(bp++);

	if(*bp == '\n' || !*bp)
		paren = 0;

	if(*bp == '\n' || !*bp || *bp == '\"')
		quote = false;

	if(*bp == '\\' && bp[1])
	{
		++bp;
		switch(*bp)
		{
		case 0:
			break;
		case 't':
			*(cp++) = '\t';
			++bp;
			break;
		case 'b':
			*(cp++) = '\b';
			++bp;
			break;
		case 'n':
			*(cp++) = '\n';
			++bp;
			break;
		default:
			*(cp++) = *(bp++);
		}
		goto requote;
	}

	if(*bp == '\n' || *bp == '\"')
		++bp;

	*cp = 0;
	return base;
}

int ScriptCompiler::compile(const char *scrname)
{
	char buffer[129];
	char *token;
	char *ext;

#ifdef	WIN32
	char *l1, *l2;
	setString(buffer, sizeof(buffer), scrname);
	l1 = strrchr(buffer, '/');
	l2 = strrchr(buffer, '\\');
	if(l1 > l2)
		token = l1;
	else
		token = l2;
#else
	setString(buffer, sizeof(buffer), scrname);
	token = strrchr(buffer, '/');
#endif

	if(!token)
		token = buffer;
	else
		++token;

	ext = strrchr(token, '.');
	if(ext)
	{
		if(strstr(exec_extensions, ext) == NULL)
			*ext = 0;
	}

	return compile(scrname, token);
}

Script::Name *ScriptCompiler::include(const char *token)
{
	char buffer[256];
	const char *local = cmds->getLast("binclude");
	const char *prefix = cmds->getLast("include");
	const char *cp;
	Name *inc = getScript(token);

	if(inc)
		return inc;

	if(!prefix)
		return NULL;

	snprintf(buffer, sizeof(buffer), "virtual.%s", token);
	cp = cmds->getLast(buffer);

	if(local)
	{
		if(cp)
			snprintf(buffer, sizeof(buffer),
				"%s/%s_%s.mac", local, token, cp);
		else
			snprintf(buffer, sizeof(buffer),
				"%s/%s.mac", local, token);
		if(!isFile(buffer) || !canAccess(buffer))
			local = NULL;
	}

	if(!local)
	{
		if(cp)
			snprintf(buffer, sizeof(buffer),
				"%s/%s_%s.mac", prefix, token, cp);
		else
			snprintf(buffer, sizeof(buffer),
				"%s/%s.mac", prefix, token);
		if(!isFile(buffer) || !canAccess(buffer))
			return NULL;
	}

	compile(buffer, (char *)token);
	return getScript(token);
}

int ScriptCompiler::compileDefinitions(const char *filename)
{
	char buffer[128];
	int rtn;

	const char *cp = strrchr(filename, '.');
	if(!cp || stricmp(cp, ".def"))
		return 0;

	cp = strrchr(filename, '/');
#ifdef	WIN32
	if(!cp)
		cp = strrchr(filename, '\\');
	if(!cp)
		cp = strchr(filename, ':');
#endif

	if(!cp)
	{
		cp = cmds->getLast("include");
		if(cp)
		{
			snprintf(buffer, sizeof(buffer), "%s/%s",
				cp, filename);
			filename = buffer;
		}
	}

	if(!isFile(filename) || !canAccess(filename))
		return 0;

	scrSource.open(filename);
	if(!scrSource.is_open())
		return 0;

	Script::use_definitions = true;

	rtn = compile((istream *)&scrSource, "definitions", filename);
	scrSource.close();
	scrSource.clear();
	return rtn;
}

int ScriptCompiler::compile(const char *scrname, char *name)
{
	int rtn;

	scrSource.open(scrname);
	if(!scrSource.is_open())
		return 0;

	rtn = compile((istream *)&scrSource, name, scrname);
	scrSource.close();
	scrSource.clear();
	return rtn;
}

int ScriptCompiler::compile(istream *str, char *name, const char *scrname)
{
	const char *errmsg = NULL;
	const char *basename = name;
	char filename[65];
	unsigned lnum = 0;
	char namebuf[256];
	char gvarname[128];
	char path[128];
	char csname[128];
	char *command, *token, *pretoken = NULL;
	char *args[SCRIPT_MAX_ARGS + 1];
	int maxargs = SCRIPT_MAX_ARGS;
	const char *err;
	char *cp = (char *)strrchr(scrname, '.');
//	const char *var = cmds->getLast("datafiles");
	bool trapflag;
	int argc, key, tlen, initkey = 0, initcount = 0;
	unsigned i;
	unsigned short count, number, total = 0;
	size_t gvarlen;
	NamedEvent *events, *esave;
	Name *script, *ds;
	Line *line, *last;
	unsigned long addmask, submask, trapmask, mask, cmask = 0;
	Method handler;
	unsigned char loopid, looplevel;
	size_t offset = 0;
	Name *base = NULL, *init = NULL;
	bool sub = false;
	streampos pos;
	bool ignore;
	scrAccess access = scrPUBLIC;
	char temp[64];
	char *pmode = NULL;
	char *ftoken = NULL;
	char *filter;
	bool bm = false;
	bool first = true;
	unsigned long addPmask = 0, subPmask = (unsigned long)~0;
	unsigned long addPmask1 = 0, subPmask1 = (unsigned long)~0;
	char catchname[256];
	const char *embed = NULL;
	bool mscmd = false, execflag = false;
	bool apps = false;
	bool defs = false;
	bool ripple = cmds->ripple;
	bool wrapper = false;
	bool keydata = false;
	merge_t *merge;
	char *tok;

	if(strstr(".bat.cmd", cp))
		mscmd = true;

	if(strstr(apps_extensions, cp))
		apps = true;
	else if(strstr(exec_extensions, cp))
	{
		snprintf(namebuf, sizeof(namebuf), "%s:%s", exec_prefix, name);
		name = namebuf;
		embed = exec_token;
		wrapper = true;
	}

	if(!strnicmp(name, exec_prefix, strlen(exec_prefix)))
		wrapper = true;

	buffer = (char *)malloc(512);
	bufsize = 512;

	scrStream = str;

	if(cp && !stricmp(cp, ".mac"))
	{
		bm = true;
		ripple = false;
	}

	if(cp && !stricmp(cp, ".scr"))
		ripple = false;

#ifdef	WIN32
	if(cp && !stricmp(cp, ".ini"))
		ripple = true;
#endif

	if(cp && !stricmp(cp, ".conf"))
		ripple = true;

	if(cp && !stricmp(cp, ".def"))
	{
		ripple = false;
		defs = true;
	}

	if(bm || defs)
		access = scrPROTECTED;

	gvarname[0] = '%';

	snprintf(gvarname + 1, 56, "%s.", name);

	gvarlen = strlen(gvarname);

#ifdef	WIN32
	const char *l1 = strrchr(scrname, '/');
	const char *l2 = strrchr(scrname, '\\');
	if(l1 > l2)
		cp = (char *)l1;
	else
		cp = (char *)l2;
#else
	cp = (char *)strrchr(scrname, '/');
#endif
	if(cp)
		++cp;
	else
		cp = (char *)scrname;
	snprintf(filename, sizeof(filename), "%s", cp);

compile:
	trapflag = false;
	count = number = 0;
	last = NULL;
	addmask = submask = trapmask = 0;
	handler = NULL;
	loopid = looplevel = 0;
	bool then = false;
	events = NULL;
	keydata = false;

	setString(csname, sizeof(csname), name);
	cp = strstr(csname, "::");

	if(cp && !stricmp(cp, "::main") && !ripple)
	{
		initkey = SCRIPT_INDEX_SIZE;
		*cp = 0;
	}

	if(ripple && cp)
		initkey = SCRIPT_INDEX_SIZE;

	key = Script::getIndex(csname);

	if(first && (bm || defs))
		initkey = SCRIPT_INDEX_SIZE;
	else if(first)
		initkey = key;

	current = script = (Name *)alloc(sizeof(Name));
	memset(script, 0, sizeof(Name));
	script->name = alloc(csname);
	script->mask = 0;
	script->events = NULL;
	addPmask1 = addPmask;
	subPmask1 = subPmask;
	addPmask = 0;
	subPmask = (unsigned long)~0;

	if(!first)
		script->next = index[key];

	script->filename = alloc(filename);

	if(first)
		init = script;

	if(pmode)
	{
		if(!strnicmp(pmode, "pub", 3) || !stricmp(pmode, "program"))
			script->access = scrPUBLIC;
		else if(!strnicmp(pmode, "priv", 4) || !stricmp(pmode, "state"))
			script->access = scrPRIVATE;
		else if(!strnicmp(pmode, "prot", 4))
			script->access = scrPROTECTED;
		else if(!strnicmp(pmode, "fun", 3))
			script->access = scrFUNCTION;
		else if(!stricmp(pmode, "local"))
			script->access = scrLOCAL;
		else
			script->access = access;
	}
	else
		script->access = access;

	if(!first)
		index[key] = script;

	pmode = NULL;

	if(!base)
		base = script;

	if(sub)
	{
		sub = false;
		memcpy(script->trap, base->trap, sizeof(base->trap));
	}

	for(;;)
	{
		if(ftoken)
			goto first;

		quote = false;
		paren = 0;
		if(!then)
		{
			cmask = 0;

			if(offset > bufsize - 160)
			{
				bufsize += 512;
				buffer = (char *)realloc(buffer, bufsize);
			}

			scrStream->getline(buffer + offset, bufsize - 1 - offset);
			if(scrStream->eof())
			{
				if(keydata && !script->first)
					goto keytoken;
				else if(wrapper && !execflag)
				{
					wrapper = false;
					execflag = true;
					embed = NULL;
					apps = false;
					setString(buffer, 16, "exec");
				}
				else if(wrapper || (!total && !count && !offset))
				{
					wrapper = false;
					embed = NULL;
					if(ripple && !strchr(name, ':'))
						break;
					setString(buffer, 16, exit_token);
				}
				else
					break;
			}
			++lnum;
			bp = strrchr(buffer, '\\');
			if(bp)
			{
				++bp;
				if(isspace(*bp) || !*bp)
				{
					if(!embed)
						offset = bp - buffer - 1;
					continue;
				}
			}

			bp = buffer;
			while(isspace(*bp))
				++bp;

			if(!*bp && keydata)
				continue;

			if(*bp == '#' && keydata)
				continue;

			if(!strnicmp(bp, "%%", 2) && keydata)
				continue;

			if(isalpha(*bp) && ripple && !script->first)
			{
				while(isalnum(*bp))
					++bp;

				while(isspace(*bp))
					++bp;

				if(*bp == '=')
				{
					snprintf(namebuf, sizeof(namebuf), "%s.%s",
						script->name, strtok_r(buffer, " \t\r\n=", &tok));
					cp = ++bp;

					while(isspace(*cp))
						++cp;

					bp = bp + strlen(bp) - 1;
					while(bp > cp && isspace(*bp))
						*(bp--) = 0;

					if(*cp == '\"' && cp[strlen(cp) - 1] == '\"')
					{
						cp[strlen(cp) - 1] = 0;
						++cp;
					}
					else if(*cp == '\'' && cp[strlen(cp) - 1] == '\'')
					{
						cp[strlen(cp) - 1] = 0;
						++cp;
					}
					else if(*cp == '{' && cp[strlen(cp) - 1] == '}')
					{
						cp[strlen(cp) - 1] = 0;
						++cp;
					}

					setValue(namebuf, cp);
					keydata = true;
					continue;
				}
			}

keytoken:
			if(keydata && !script->first)
			{
				command = "_keydata_";
				handler = cmds->getHandler("_keydata_");
				keydata = false;
			}
			else
				handler = NULL;

			if(handler)
			{
				line = (Line *)alloc(sizeof(Line));
				line->cmd = command;
				line->line = number++;
				line->lnum = lnum;
				line->cmask = cmask;
				line->mask = script->mask;
				line->next = NULL;
				line->args = (const char **)alloc(sizeof(char *));
				line->argc = 0;
				line->args[0] = NULL;
				line->scr.method = handler;

				err = cmds->check(command, line, this);
				if(err)
				{
					if(*err)
						slog.error("%s(%d): %s: %s", filename, lnum, command, err);
				}
				else
				{
					// second copy in case compile-time registration
					if(line->scr.method != handler)
					{
						line = (Line *)memcpy(alloc(sizeof(Line)), line, sizeof(Line));
						line->scr.method = handler;
					}
					++count;
					if(!script->first)
						script->first = line;
					else
						last->next = line;
					last = line;
				}
			}

			bp = buffer;
			while(isspace(*bp) && keydata)
				++bp;

			if(!*bp && keydata)
				continue;

			bp = buffer;
			if(embed)
			{
				if(!mscmd)
				{
					if(*bp != '#')
						continue;

					++bp;
					if(strnicmp(embed, bp, strlen(embed)))
						continue;
				}

				if(mscmd)
				{
					while(isspace(*bp))
						++bp;

					if(*bp == '@')
						++bp;

					if(stricmp(bp, "rem"))
						continue;

					bp += 3;
					while(isspace(*bp))
						++bp;

					if(strnicmp(embed, bp, strlen(embed)))
						continue;
				}

				bp += strlen(embed);
				if(isalnum(*bp))
				{
					--bp;
					*bp = ' ';
				}
			}
		}
		else
			then = false;

		offset = 0;
		++number;
first:
		while(NULL != (token = getToken(&ftoken)))
		{
			if(*token == '~')
			{
		                esave = (NamedEvent *)alloc(sizeof(NamedEvent));
				esave->name = alloc(token + 1);
				esave->line = NULL;
				esave->next = events;
				esave->type = '~';
				events = esave;
				continue;
			}
			if(*token == '@' || *token == '{')
			{
				esave = (NamedEvent *)alloc(sizeof(NamedEvent));
				esave->name = alloc(token + 1);
				esave->line = NULL;
				esave->next = events;
				esave->type = '@';
				events = esave;
				continue;
			}

			if(!ripple && !embed && !apps)
  			  if(!stricmp(token, "private") || !stricmp(token,
"protected") || !stricmp(token, "public") || !stricmp(token, "program") ||
!stricmp(token, "module"))
			{
				if(!stricmp(token, "module"))
				{
					ftoken = "fconst";
					token = "program";
				}
				pmode = alloc(token);
repname1:
				name = getToken();

				if(!name)
					break;
				if(*name == '+')
				{
					if(!stricmp(name, "+dtmf"))
						addPmask |= 0x08;
					else
						addPmask |= cmds->getTrapModifier(name + 1);
					goto repname1;
				}

				if(*name == '-')
				{
					subPmask &= ~cmds->getTrapModifier(name + 1);
					goto repname1;
				}

				token = "::";
				break;
			}

			if(!strnicmp(token, "func", 4) && !ripple && !embed && !apps && !strchr(token, ':'))
			{
				ftoken = "fconst";
				pmode = "function";
				goto repname1;
			}


			if(!stricmp(token, "macro") && !ripple && !embed && !apps && !strchr(token, ':'))
			{
				ftoken = "fconst";
				pmode = "function";
				goto repname1;
			}

			if(!stricmp(token, "local") && !ripple && !embed && !apps && !strchr(token, ':'))
			{
				ftoken = "fconst";
				pmode = "local";
				goto repname1;
			}

			if(!strnicmp(token, "proc", 4) && !ripple && !embed && !apps && !strchr(token, ':'))
			{
				pmode = "function";
				goto repname1;
			}

			if(!stricmp(token, "catch") && !ripple && !embed && !apps)
			{
				ftoken = "fconst";
				name = getToken();
				if(*name == '^')
					snprintf(catchname, sizeof(catchname),
						"-catch-signal:%s", ++name);
				else if(*name == '@')
					snprintf(catchname, sizeof(catchname),
						"-catch-%s", ++name);
				else
					snprintf(catchname, sizeof(catchname),
						"-catch-%s", name);
				name = catchname;
				pmode = "function";
				token = "::";
				break;
			}

			if((ripple || apps) && *token == '[')
			{
				if(use_funcs)
					pmode = "function";
				else
					pmode = "public";

				name = token + 1;
				cp = strchr(name, ']');
				if(cp)
					*cp = 0;
				token = "::";
				break;
			}

			tlen = (int)strlen(token);
			if(token[tlen - 1] == ':' && !ripple && !apps && !embed)
			{
				token[tlen - 1] = 0;
				name = token;
				token = "::";
				break;
			}


			if(*token == '^')
			{
				if(!trapflag)
				{
					trapmask = 0;
					trapflag = true;
				}
			}

			if(!stricmp(token, "->") && !last)
			{
				token = "goto";
				break;
			}

			if(*token != '^' && *token != '+' && *token != '-' && *token != '?')
				break;

			if(*token == '^')
				mask = cmds->getTrapMask(token + 1);
			else
				mask = cmds->getTrapModifier(token + 1);

			if(!mask)
			{
				slog.error("%s(%d): %s: unknown trap id", filename, lnum, token + 1);
				continue;
			}

			switch(*token)
			{
			case '^':
				last = NULL;
				script->mask |= mask | cmds->getTrapDefault();
				trapmask |= mask;
				break;
			case '+':
				addmask |= mask;
				break;
			case '-':
				submask |= mask;
				break;
			case '?':
				cmask |= mask;
			}
		}

		if(!token)
			continue;

		if(!stricmp(token, "::") && !embed)
			break;

		if(!strnicmp(token, "exec.", 5) && (embed || apps))
			execflag = true;

		if(!stricmp(token, "exec") && (embed || apps))
			execflag = true;

		if(!strnicmp(token, "exec.", 5) && !execflag && exec_token)
			goto noexec;

		if(!stricmp(token, "exec") && !execflag && exec_token)
		{
noexec:
			slog.error("%s(%d): exec only used in embedded", filename, lnum);
			continue;
		}

		if(!stricmp(token, "disuse") && defs)
		{
			if(NULL != (token = getToken()))
			{
				snprintf(temp, sizeof(temp), "use.%s", token);
				cmds->setValue(temp, "none");
				continue;
			}
			token = "disuse";
		}

		if(!stricmp(token, "use"))
		{
			if(NULL != (token = getToken()))
			{
				snprintf(temp, sizeof(temp), "use.%s", token);
				cp = (char *)cmds->getLast(temp);
				if(cp)
					token = cp;

				if(!stricmp(token, "none"))
					continue;

				if(!Script::use(token))
					slog.warn("%s(%d): %s: package missing", filename, lnum, token);
			}
			else
			{
				slog.error("%s(%d): use: name missing", filename, lnum);
				continue;
			}
			snprintf(catchname, sizeof(catchname), "use.%s", token);
			token = catchname;
		}
		else if(!stricmp(token, "virtual") && !embed && (!ripple || use_macros) && scrStream == (istream *)&scrSource)
		{
			token = getToken();
			if(!token)
				continue;

			if(strchr(token, '/'))
				continue;

			if(inccount > 255)
				continue;

			snprintf(temp, sizeof(temp), "virtual.%s", token);
			if(!cmds->getLast(temp))
			{
				cp = getToken();
				if(!cp || !*cp)
					cp = "none";
				cmds->setValue(temp, "cp");
			}

			incfiles[inccount++] = alloc(token);
			continue;
		}
		else if(!stricmp(token, "include") && use_merge && !apps && !embed)
		{
			token = getToken();
			if(!token)
				continue;

			if(!strchr(token, ':'))
			{
				snprintf(temp, sizeof(temp), name);
				cp = strchr(temp, ':');
				if(cp)
					*cp = 0;
				addString(temp, sizeof(temp), "::");
				addString(temp, sizeof(temp), token);
				token = temp;
			}
			token = alloc(token);

			filter = getToken();
			for(;;)
			{
				if(filter)
				{
					snprintf(temp, sizeof(temp), "%s", filter);
					cp = strchr(temp, ':');
					if(cp)
						*cp = 0;
					addString(temp, sizeof(temp), ":");
					filter = alloc(temp);
				}
				else
					filter = "";

				merge = (merge_t *)alloc(sizeof(merge_t));
				merge->next = mlist;
				merge->target = script;
				merge->source = token;
				merge->prefix = filter;
				mlist = merge;
				filter = getToken();
				if(!filter)
					break;
			}
			continue;
		}
		else if((!stricmp(token, "requires") || !stricmp(token, "import")) && scrStream == (istream *)&scrSource && (!ripple || use_macros) && !apps && !embed)
		{
			token = getToken();
			if(!token)
				continue;

			if(strchr(token, '/'))
				continue;


			if(inccount > 255)
				continue;
			incfiles[inccount++] = alloc(token);
   			continue;
		}

		ignore = false;

		if(!token)
			continue;

		if(*token == '%' && !ripple)
		{
			pretoken = token;
			token = "expr";
		}
		else if(*token == '&' && !ripple && !apps)
		{
			pretoken = token;
			token = "call";
		}
		else if(!strnicmp(token, "*::", 3) && !ripple && !apps)
		{
			pretoken = token + 3;
			token = "call";
		}
		else if(!strnicmp(token, "::", 2) && !ripple && !apps)
		{
			pretoken = token + 2;
			token = "call";
		}
		else if(strstr(token, "::") && (!ripple || use_macros) && !apps)
		{
			pretoken = token;
			token = "call";
		}
		else
			pretoken = NULL;

		if(*token == '@')
		{
			ignore = true;
			++token;
		}

		trapflag = false;
		handler = cmds->getHandler(token);
		if(handler == (Method)NULL && use_definitions)
		{
			snprintf(temp, sizeof(temp), "definitions::%s", token);
			ds = getScript(temp);
			if(ds && ds->access == scrFUNCTION)
			{
				pretoken = alloc(temp);
				token = "call";
				handler = cmds->getHandler(token);
			}
		}
		if(handler == (Method)NULL)
		{
			errmsg = preproc(token);
			if(!errmsg)
				continue;

			addmask = submask = 0;
			if(!ignore)
				slog.error("%s(%d): %s: %s", filename, lnum, token, errmsg);
			continue;
		}

		command = alloc(token);
		argc = 0;
		while(argc < maxargs && NULL != (token = getToken(&pretoken)))
		{
			if(token[0] == '$' && token[1] == '{' && token[strlen(token) - 1] == '}')
			{
				token[strlen(token) - 1] = 0;
				++token;
				*token = '$';
				goto insert;
				
			}
			if(token[0] == '$' && isalpha(token[1]))
			{
insert:
				if(!stricmp(token, "$script.name"))
					token = alloc((char *)basename);
				else if(!stricmp(token, "$script.file"))
					token = alloc((char *)name);
				else if(!stricmp(token, "$script.line"))
				{
					sprintf(temp, "%d", number);
					token = alloc(temp);
				}
				else
				{
					if(!strchr(++token, '.'))
					{
						snprintf(path, sizeof(path), "%s.%s", basename, token);
						token = path;
					}
					token = (char *)getDefined(token);
				}

				if(!token)
					token = "";
			}
			else if(token[0] == 0x01)
			{
				token[0]='{';
				token = alloc(token);
			}
			else if(token[0] == '.' && isalpha(token[1]))
			{
				cp = token + strlen(token) - 1;
				if(*cp != '.')
				{
					gvarname[0] = '%';
					snprintf(gvarname + gvarlen, sizeof(gvarname) - gvarlen, "%s", token + 1);
					token = alloc(gvarname);
				}
				else
					token = alloc(token);
			}
			else if(!strnicmp(token, "=.", 2))
			{
				gvarname[0] = '=';
				snprintf(gvarname + gvarlen, sizeof(gvarname) - gvarlen, "%s", token + 2);
				token = alloc(gvarname);
			}
			else if(!strnicmp(token, "#.", 2))
			{
				gvarname[0] = '#';
				snprintf(gvarname + gvarlen, sizeof(gvarname) - gvarlen, "%s", token + 2);
				token = alloc(gvarname);
			}
			else if(!strnicmp(token, "&.", 2) || !strnicmp(token, ">.", 2))
			{
				gvarname[0] = '&';
				snprintf(gvarname + gvarlen,sizeof(gvarname) - gvarlen, "%s", token + 2);
				token = alloc(gvarname);
			}
			else if(!strnicmp(token, "@.", 2))
			{
				gvarname[0] = '@';
				snprintf(gvarname + gvarlen,sizeof(gvarname) - gvarlen, "%s", token + 2);
				token = alloc(gvarname);
			}
			else if(!strnicmp(token, "%.", 2))
			{
				gvarname[0] = '%';
				snprintf(gvarname + gvarlen, sizeof(gvarname) - gvarlen, "%s", token + 2);
				token = alloc(gvarname);
			}
			else if(*token == '>' && isalnum(token[1]))
			{
				token = alloc(token);
				*token = '&';
			}
			else
				token = alloc(token);


			args[argc++] = token;

			if(!stricmp(token, "then") && handler == (Method)&ScriptMethods::scrIf)
			{
				--bp;
				*bp = ' ';
				then = true;
				handler = (Method)&ScriptMethods::scrIfThen;
				break;
			}
		}

		args[argc++] = NULL;
		line = (Line *)alloc(sizeof(Line));
		line->line = number;
		line->lnum = lnum;
		line->cmask = cmask;
		line->mask = ((~0 & ~trapmask) | addmask) & ~submask;
		if(script->mask)
			line->mask &= cmds->getTrapHandler(script);
		if(!trapmask)
		{
			line->mask |= addPmask1;
			line->mask &= subPmask1;
		}
		line->next = NULL;
		line->args = (const char **)alloc(sizeof(char *) * argc);
		line->argc = --argc;
		line->scr.method = handler;
		line->cmd = command;
		line->loop = 0;
		if(cmds->isInitial(line->cmd))
			line->loop = 0xffff;

		addmask = submask = 0;

		if(!stricmp(command, "repeat") || !stricmp(command, "for") || !stricmp(command, "do") || !stricmp(command, "foreach"))
		{
			if(!looplevel)
				++loopid;
			++looplevel;
			line->loop = loopid * 128 + looplevel;
		}

		if(!stricmp(command, "loop") || !strnicmp(command, "loop.", 5))
		{
			line->loop = loopid * 128 + looplevel;
			if(!looplevel)
			{
				slog.error("%s(%d): loop nesting error", filename, line->lnum);
				continue;
			}
			else
				--looplevel;
		}

		memcpy(line->args, &args, sizeof(char *) * argc);

		err = cmds->check(command, line, this);
		if(err)
		{
			if(*err)
				slog.error("%s(%d): %s: %s", filename, lnum, command, err);
			continue;
		}

		++count;
		script->mask |= trapmask;
		if(!script->first)
			script->first = line;

		while(events)
		{
			esave = events->next;
			events->line = line;
			events->next = script->events;
			script->events = events;
			events = esave;
		}

		if(trapmask && !last)
		{
			for(i = 0; i < TRAP_BITS; ++i)
			{
				if((1l << i) & trapmask)
				{
					if(!script->trap[i])
						script->trap[i] = line;
				}
			}
		}

		if(last)
			last->next = line;

		last = line;
	}
	line = script->first;
	if(!script->mask)
		script->mask = cmds->getTrapDefault();
	script->mask |= addPmask1;
	script->mask &= subPmask1;

	addPmask1 = 0;
	subPmask1 = (unsigned long)~0;
	while(line)
	{
		line->mask &= script->mask;
		line->mask |= ((~script->mask) & cmds->imask);
		line = line->next;
	}
	total += count;

	checkSegment(script);

	if(first && count)
		initcount = count;
	else if(count)
		slog.info("compiled %s; %d steps", script->name, count);
	first = false;

	if(!scrStream->eof())
	{
		execflag = false;
		if(apps && apps_prefix)
			snprintf(namebuf, sizeof(namebuf), "%s::%s", apps_prefix, name);
		else if(strstr(name, "::") || (ripple && !use_prefix) || apps)
			snprintf(namebuf, sizeof(namebuf), "%s", name);
		else
			snprintf(namebuf, sizeof(namebuf), "%s::%s", basename, name);
		name = namebuf;
		if(!strnicmp(name, "exec:", 5) && exec_funcs)
			pmode = "function";
		goto compile;
	}
	scrSource.close();
	scrSource.clear();
	if(init)
	{
		line = last = NULL;
		if(initkey == SCRIPT_INDEX_SIZE && initcount)
			line = init->first;

		while(line)
		{
			if(line->loop != 0xffff)
			{
				slog.error("%s(%d): %s: not allowed as initializer",filename, line->lnum, line->cmd);
				if(last)
					last->next = line->next;
				else
					first = true;
			}
			line = line->next;
		}
		if(initkey == SCRIPT_INDEX_SIZE && initcount)
			slog.info("compiled %s initializer; %d steps", init->name, initcount);
		else if(initcount)
			slog.info("compiled %s; %d steps", init->name, initcount);
		init->next = index[initkey];
		index[initkey] = init;
	}
	free(buffer);
	return total;
}




syntax highlighted by Code2HTML, v. 0.9.1