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

#if defined(_MSC_VER) && _MSC_VER >= 1300
#if defined(_WIN64_) || defined(__WIN64__)
#define RLL_SUFFIX ".x64"
#elif defined(_M_IX86)
#define RLL_SUFFIX ".x86"
#else
#define RLL_SUFFIX ".xlo"
#endif
#endif

#if defined(__MINGW32__) | defined(__CYGWIN32__)
#define RLL_SUFFIX ".dso"
#endif

#ifdef  W32
#ifndef RLL_SUFFIX
#define RLL_SUFFIX ".rll"
#endif
#endif

#ifndef RLL_SUFFIX
#define RLL_SUFFIX ".dso"
#endif

using namespace std;
using namespace ost;

bool Script::fastStart = true;
unsigned Script::fastStepping = 32;
unsigned Script::autoStepping = 1;
bool Script::useBigmem = true;
size_t Script::pagesize = 1024;
unsigned Script::symsize = 64;
unsigned Script::symlimit = 960;
char Script::decimal = '.';
bool Script::use_definitions = false;
bool Script::use_macros = false;
bool Script::use_prefix = false;
bool Script::use_merge = false;
bool Script::use_funcs = false;
const char *Script::plugins = NULL;
const char *Script::altplugins = NULL;

const char *Script::etc_prefix = ".";
const char *Script::var_prefix = ".";
const char *Script::log_prefix = ".";

const char *Script::access_user = NULL;
const char *Script::access_pass = NULL;
const char *Script::access_host = "localhost";

#ifdef	WIN32
const char *Script::apps_extensions = ".app.apps";
const char *Script::exec_extensions = ".bat";
#else
const char *Script::apps_extensions = ".app.exec.apps";
const char *Script::exec_extensions = ".sh";
#endif
const char *Script::exec_token = "script:";
const char *Script::exec_prefix = "exec:";
const char *Script::exit_token = "exit";
const char *Script::apps_prefix = "libexec";

bool Script::exec_funcs = false;

Script::Test *Script::test = NULL;
Script::Fun *Script::ifun = NULL;
Script::Package *Script::Package::first = NULL;

class ScriptSysRegistry : public Script
{
public:
	ScriptSysRegistry();
};

static class ScriptSysRegistry registry;

#if     defined(CAPE_REGISTRY_PREFIX)
#define REGISTRY_SCRIPT_SETTINGS CAPE_REGISTRY_PREFIX "\\Script Settings"
#else
#define REGISTRY_SCRIPT_SETTINGS "SOFTWARE\\CAPE Framework\\Script Settings"
#endif

ScriptSysRegistry::ScriptSysRegistry()
{
#ifdef	WIN32
	static TCHAR regpath[256];
	LONG value;
	HKEY key;
	char *env;

	Script::plugins =
		"C:/Program Files/Common Files/GNU Telephony/Script Plugins";

#ifdef	DEBUG
#define	PLUGINS "Debug"
#else
#define	PLUGINS "Plugins"
#endif

	if(RegOpenKey(HKEY_LOCAL_MACHINE, REGISTRY_SCRIPT_SETTINGS, &key) == ERROR_SUCCESS)
	{
		if(RegQueryValue(key, PLUGINS, regpath, &value) == ERROR_SUCCESS)
		{
			Script::plugins = regpath;
			env = regpath;
			while(NULL != (env = strchr(env, '\\')))
				*env = '/';
		}

		if(RegQueryValue(key, "paging", NULL, &value) == ERROR_SUCCESS)
		{
			pagesize = value;
			symlimit = value - sizeof(Symbol) - 32;
		}

		if(RegQueryValue(key, "symsize", NULL, &value) == ERROR_SUCCESS)
			symsize = value;

		if(RegQueryValue(key, "autostep", NULL, &value) == ERROR_SUCCESS)
			autoStepping = value;

			if(RegQueryValue(key, "faststep", NULL, &value) == ERROR_SUCCESS)
			 fastStepping = value;

		RegCloseKey(key);
	}
#else
	plugins = SCRIPT_LIBPATH;
#endif
}

bool Script::isScript(Name *scr)
{
	const char *ext = strrchr(scr->filename, '.');
	if(!ext)
		return false;

	if(!stricmp(ext, ".scr"))
		return true;

	if(!stricmp(ext, ".mac"))
		return true;

	return false;
}

bool Script::isSymbol(const char *id)
{
	if(*id == '%' || *id == '&' || *id == '@')
		return true;

	return false;
}

bool Script::isFunction(Name *scr)
{
	switch(scr->access)
	{
	case scrFUNCTION:
	case scrLOCAL:
		return true;
	default:
		return false;
	}
}

bool Script::isPrivate(Name *scr)
{
	switch(scr->access)
	{
	case scrLOCAL:
	case scrPRIVATE:
		return true;
	default:
		return false;
	}
}

unsigned Script::getIndex(const char *id)
{
	unsigned int key = 0;

	while(*id)
		key ^= (key << 1) ^ (*(id++) & 0x1f);

	return key % SCRIPT_INDEX_SIZE;
}

Script::Symbol *Script::deref(Symbol *sym)
{
	while(sym && sym->type == symREF)
		memcpy(&sym, sym->data, sizeof(sym));

	return sym;
}

bool Script::symindex(Symbol *sym, short index)
{
	Array *a;
	if(!sym)
		return false;

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

	switch(sym->type)
	{
	case symFIFO:
		if(index < 0)
			index = a->tail + index;
		if(index < 0 || index > a->tail)
			return false;
		a->head = index;
		if(a->head == a->tail)
			a->head = a->tail = 0;
		return true;
	case symSTACK:
		if(index < 0)
			index = a->tail + index;
		if(index < 0 || index > a->tail)
			return false;
		a->tail = index;
		return true;
	case symARRAY:
		if(index < 0)
			index = a->tail + index;
		if(index < 0 || index >= a->count)
			return false;
		a->head = index;
		return true;
	default:
		return false;
	}
}

void Script::clear(Symbol *sym)
{
	unsigned dec;
	unsigned pos = 1;
	Array *a = (Array *)&sym->data;
	ScriptProperty *p;

	switch(sym->type)
	{
	case symARRAY:
	case symSTACK:
	case symFIFO:
		a->head = a->tail = 0;
		sym->data[sizeof(Array)] = 0;
		return;
	case symBOOL:
		sym->data[0] = 'n';
		sym->data[1] = 0;
		return;
	case symNUMBER:
		dec = sym->size - 11;
		if(dec)
			++dec;
		sym->data[0] = '0';
		if(dec)
		{
			sym->data[pos++] = decimal;
			while(pos < dec)
				sym->data[pos++] = '0';
		}
		sym->data[pos] = 0;
		return;
	case symPROPERTY:
		memcpy(&p, &sym->data, sizeof(p));
		p->clear(sym->data + sizeof(p), sym->size - sizeof(p));
		return;
	case symMODIFIED:
		sym->type = symORIGINAL;
	case symORIGINAL:
	case symLOCK:
	case symNORMAL:
		if(!stricmp(sym->id, "script.error"))
			strcpy(sym->data, "none");
		else
			sym->data[0] = 0;
		return;
	case symSEQUENCE:
		sym->data[sym->size] = 0;
		return;
	case symCOUNTER:
		sym->data[0] = '0';
		sym->data[1] = 0;
		return;
	case symTIMER:
		sym->data[0] = 0;
	default:
		return;
	}
}

unsigned Script::count(Symbol *sym)
{
	Array *a = (Array *)&sym->data;

	switch(sym->type)
	{
	case symCONST:
	case symCOUNTER:
	case symTIMER:
	case symLOCK:
		return 0;
	case symARRAY:
		return a->count;
	case symSTACK:
	case symFIFO:
		return a->count - 1;
	default:
		return 1;
	}
}

unsigned Script::storage(Symbol *sym)
{
	Array *a = (Array *)&sym->data;

	switch(sym->type)
	{
	case symARRAY:
	case symSTACK:
	case symFIFO:
		return a->rec;
	case symPROPERTY:
		return sym->size - sizeof(ScriptProperty *);
	case symORIGINAL:
	case symMODIFIED:
	case symINITIAL:
	case symNORMAL:
		return sym->size;
	default:
		return 0;
	}
}

const char *Script::extract(Symbol *sym)
{
	long value;
	const char *data;
	unsigned short pos, len;
	Array *a;
	time_t now;

	if(!sym)
		return NULL;

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

	switch(sym->type)
	{
	case symLOCK:
		data = strchr(sym->data, ':');
		if(data)
			return ++data;
		return NULL;
	case symPROPERTY:
		return sym->data + sizeof(ScriptProperty *);
	case symORIGINAL:
	case symMODIFIED:
	case symNORMAL:
	case symCONST:
	case symNUMBER:
	case symBOOL:
		return sym->data;
	case symTIMER:
		if(sym->data[0])
		{
			time(&now);
			snprintf(sym->data + 12, 12, "%ld",  now - atol(sym->data));
		}
		else
			setString(sym->data + 12, 12, "0");
		return sym->data + 12;
	case symCOUNTER:
		value = atoi(sym->data);
		snprintf(sym->data, sym->size + 1, "%ld", ++value);
		return sym->data;
		case symARRAY:
		pos = a->head;
		if(pos >= a->count || pos >= a->tail)
			return "";

		return sym->data + sizeof(Array) + pos * (a->rec + 1);
		case symSTACK:
		if(a->tail == a->head)
		{
			a->tail = a->head = 0;
			return "";
		}

		pos = a->tail;
		if(a->tail == 0)
			a->tail = a->count - 1;
		else
			--a->tail;

		return sym->data + sizeof(Array) + pos * (a->rec + 1);
		case symSEQUENCE:
		len = sym->size / sizeof(const char *);
		pos = sym->data[sym->size];
		memcpy(&data, &sym->data[pos * sizeof(data)], sizeof(data));
		if(++pos >= len)
			pos = 0;
		sym->data[sym->size] = (unsigned char)pos;
		return data;
		case symFIFO:
		if(a->head == a->tail)
			return "";

		data = sym->data + a->head * (a->rec + 1) + sizeof(Array);
		if(++a->head >= a->count)
			a->head = 0;
		return data;
	default:
		return NULL;
	}
}

bool Script::append(Symbol *sym, const char *value)
{
	switch(sym->type)
	{
	case symBOOL:
	case symNUMBER:
	case symPROPERTY:
	case symCOUNTER:
	case symTIMER:
		return false;
	case symORIGINAL:
		sym->type = symMODIFIED;
		sym->data[0] = 0;
	case symMODIFIED:
		addString(sym->data, sym->size + 1, value);
		return true;
	case symNORMAL:
	case symINITIAL:
		addString(sym->data, sym->size + 1, value);
		sym->type = symNORMAL;
		return true;
	default:
		return commit(sym, value);
	}
}

bool Script::commit(Symbol *sym, const char *value)
{
	Array *a;
	long val;
	unsigned short pos, npos, len;
	int dec = 0;
	char *dp;
	const char *sp;
	long v1, v2;
	char vbuf[12];
	ScriptProperty *p;
	time_t now;

	if(!sym)
		return false;

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

	switch(sym->type)
	{
	case symBOOL:
		if(atol(value))
			sym->data[0] = 'y';
		else switch(*value)
		{
		case '0':
		case 'n':
		case 'N':
		case 'f':
		case 'F':
			sym->data[0] = 'n';
		default:
			sym->data[0] = 'y';
		};
		sym->data[1] = 0;
		return true;					
	case symNUMBER:
		v1 = atol(value);
		sp = strchr(value, '.');
		if(!sp)
			sp = strchr(value, decimal);
		if(sp)
			v2 = atol(++sp);
		else
			v2 = 0;
		dp = NULL;
		if(sym->size > 11)
		{
			dec = sym->size - 12;
			snprintf(vbuf, sizeof(vbuf), "%ld", v2);
			len = (unsigned short)strlen(vbuf);
			sp = vbuf;
			if(len > dec && vbuf[dec] >= '5')
			{
				while(--dec > -1)
				{
					if(vbuf[dec] < '9')
					{
						++vbuf[dec];
						break;
					}
					vbuf[dec] = '0';
				}
				if(dec < 0 && v1 < 0)
					--v1;
				else if(dec < 0)
					++v1;
			}
			dec = sym->size - 12;
			snprintf(sym->data, 12, "%ld", v1);
					len = (unsigned short)strlen(sym->data);
			sym->data[len++] = decimal;
			dp = sym->data + len;
			while(dec--)
				sym->data[len++] = '0';
			sym->data[len] = 0;
		}
		else
		{
			if(sp && *sp >= '5')
			{
				if(v1 > 0)
					++v1;
				else
					--v1;
			}
			snprintf(sym->data, sym->size + 1, "%ld", v1);
		}
		if(sp && dp)
		{
			len = (unsigned short)strlen(sp);
			if(len > sym->size - 12)
				len = sym->size - 12;
			memcpy(dp, sp, len);
		}
		return true;
	case symARRAY:
		if(a->head >= a->count)
			return false;

		setString(sym->data + sizeof(Array) + a->head * (a->rec + 1), a->rec + 1, value);
		if(++a->head > a->count)
			a->head = a->count;
		if(a->head > a->tail)
			a->tail = a->head;
		return true;
	case symFIFO:
	case symSTACK:
		len = a->rec + 1;
		pos = a->tail;
		if(sym->type == symSTACK)
			npos = ++pos;
		else
		{
			npos = pos;
			++npos;
		}
		if(npos >= a->count)
			npos = 0;
		if(npos == a->head)
			return false;	// full

		a->tail = npos;
		setString(sym->data + sizeof(Array) + (pos * len), len,	value);
		return true;
	case symTIMER:
		if(sym->data[0] == 0)
		{
			time(&now);
			val = (long)now + atol(value);
		}
		else
			val = atol(sym->data) + atol(value);
		snprintf(sym->data, sym->size + 1, "%ld", val);
		return true;
	case symCOUNTER:
		val = atoi(value);
		snprintf(sym->data, sym->size + 1, "%ld", --val);
		return true;
	case symORIGINAL:
		sym->type = symMODIFIED;
	case symMODIFIED:
		setString(sym->data, sym->size + 1, value);
		return true;
	case symNORMAL:
	case symINITIAL:
		setString(sym->data, sym->size + 1, value);
		sym->type = symNORMAL;
		return true;
	case symPROPERTY:
		memcpy(&p, &sym->data, sizeof(p));
		p->set(value, sym->data + sizeof(p), sym->size - sizeof(p));
		return true;
	default:
		return false;
	}
}

bool Script::use(const char *name)
{
	Package *pkg = Package::first;
	char buffer[256];
	const char *dpath = plugins;
	const char *alt = altplugins;
	const char *ns = name;

retry:
	if(strchr(name, '/'))
		return false;

#ifdef	WIN32
	if(strchr(name, '\\'))
		return false;

	if(strchr(name, ':'))
		return false;

#endif
	snprintf(buffer, sizeof(buffer), "%s/%s" RLL_SUFFIX, dpath, name);
	name = buffer;

	while(pkg)
	{
		if(!strcmp(pkg->filename, name))
			return true;
		pkg = pkg->next;
	}

	if(!canAccess(name))
	{
		if(alt)
		{
			dpath = alt;
			alt = NULL;
			name = ns;
			goto retry;
		}
		slog.error() << "use: cannot find " << name << std::endl;
		return false;
	}


	pkg = new Package(name);
	if(pkg->isValid())
		return true;

	slog.error() << "use: cannot load " << name << std::endl;
	delete pkg;
	return false;
}

Script::Package::Package(const char *name) :
DSO(name)
{
	filename = newString(name);
	next = first;
	first = this;
}

void Script::addFunction(const char *id, unsigned args, Script::Function handler)
{
	Script::Fun *rf = new Script::Fun;
	rf->id = id;
	rf->args = args;
	rf->fn = handler;
	rf->next = ifun;
	ifun = rf;
}

void Script::addConditional(const char *id, Script::Cond handler)
{
	Script::Test *rt = new Script::Test;

	rt->id = id;
	rt->handler = handler;
	rt->next = test;
	test = rt;
}



syntax highlighted by Code2HTML, v. 0.9.1