// 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;
ScriptInterp::ScriptInterp() :
Mutex(), ScriptSymbols()
{
stack = 0;
cmd = NULL;
image = NULL;
memset(temps, 0, sizeof(temps));
tempidx = 0;
session = this;
thread = NULL;
trace = false;
lock = NULL;
sequence = 0;
setString(logname, sizeof(logname), "ccscript");
}
unsigned ScriptInterp::getTempSize(void)
{
return symsize + 1;
}
char *ScriptInterp::getTemp(void)
{
char *tmp = temps[tempidx++];
if(tempidx >= SCRIPT_TEMP_SPACE)
tempidx = 0;
return tmp;
}
ScriptInterp *ScriptInterp::getInterp(const char *id)
{
if(!atoi(id))
return this;
return NULL;
}
unsigned ScriptInterp::getId(void)
{
return 0;
}
bool ScriptInterp::isLocked(const char *id)
{
if(!strnicmp(id, "script.", 7) && initialized)
return true;
if(!strnicmp(id, "initial.", 8) && initialized)
return true;
return false;
}
void ScriptInterp::setFrame(void)
{
frame[stack].index = 0;
updated = false;
}
const char *ScriptInterp::remapLocal(void)
{
return "script";
}
const char *ScriptInterp::getExternal(const char *opt)
{
char *cp, *p;
Line *line;
unsigned idx;
if(!cmd)
return NULL;
if(!stricmp(opt, "script.id"))
{
cp = getTemp();
snprintf(cp, symsize, "%d", getId());
return cp;
}
else if(!stricmp(opt, "script.index"))
{
if(!stack)
return "";
line = frame[stack - 1].line;
idx = frame[stack - 1].index;
cp = getTemp();
snprintf(cp, symsize, "%u", idx - 1);
return cp;
}
else if(!stricmp(opt, "script.basename"))
{
cp = getTemp();
setString(cp, symsize, frame[stack].script->name);
p = strstr(cp, "::");
if(p)
*p = 0;
return cp;
}
else if(!stricmp(opt, "script.subname"))
{
cp = getTemp();
setString(cp, symsize, frame[stack].script->name);
p = strstr(cp, "::");
if(p)
return p + 2;
return cp;
}
else if(!stricmp(opt, "script.name"))
return frame[stack].script->name;
else if(!stricmp(opt, "script.stack"))
{
cp = getTemp();
snprintf(cp, symsize, "%d", stack);
return cp;
}
else if(!stricmp(opt, "script.base"))
{
cp = getTemp();
snprintf(cp, symsize, "%d", frame[stack].base);
}
return cmd->getExternal(opt);
}
void ScriptInterp::initialize(void)
{
}
bool ScriptInterp::execute(Method method)
{
return (this->*(method))();
}
void ScriptInterp::branching(void)
{
}
Script::Name *ScriptInterp::getScript(const char *name)
{
if(!image)
return NULL;
Name *scr = image->getScript(name);
return scr;
}
void ScriptInterp::release(void)
{
if(lock)
{
lock->leaveMutex();
lock = NULL;
}
}
ScriptSymbols *ScriptInterp::getSymbols(const char *id)
{
if(strchr(id, '.') && session != this)
{
if(lock)
lock->leaveMutex();
session->enterMutex();
lock = dynamic_cast<Mutex *>(session);
}
else
release();
if(strchr(id, '.'))
return dynamic_cast<ScriptSymbols*>(session);
if(!frame[stack].local)
return dynamic_cast<ScriptSymbols*>(this);
return frame[stack].local;
}
ScriptSymbols *ScriptInterp::getLocal(void)
{
if(frame[stack].local)
return frame[stack].local;
return dynamic_cast<ScriptSymbols*>(this);
}
bool ScriptInterp::setConst(const char *id, const char *value)
{
MutexLock lock(*this);
Symbol *sym;
unsigned short len;
if(!value)
return false;
len = (unsigned short)strlen(value);
if(!len)
++len;
sym = mapSymbol(id, len);
if(!sym)
return false;
if(sym->type != symINITIAL)
return false;
sym->type = symCONST;
setString(sym->data, sym->size + 1, value);
return true;
}
char ScriptInterp::getPackToken(void)
{
const char *sym = extract(mapSymbol("script.token"));
if(!sym)
sym = ",";
if(!*sym)
sym = ",";
return *sym;
}
Script::Symbol *ScriptInterp::mapSymbol(const char *id, unsigned short size)
{
Symbol *sym;
if(*id != '@')
return mapDirect(id, size);
sym = mapDirect(++id);
if(!sym)
return NULL;
id = extract(sym);
if(!id)
return NULL;
if(!*id)
return NULL;
return mapDirect(id, size);
}
Script::Symbol *ScriptInterp::mapDirect(const char *id, unsigned short size)
{
Symbol *sym;
ScriptSymbols *syms;
unsigned count = 1;
char partial[70];
char *cp;
const char *p;
if(!id)
return NULL;
if(*id == '%' || *id == '&')
++id;
if(*id == '.' && frame[stack].script)
{
cp = (char *)strchr(frame[stack].script->filename, '.');
if(cp && !stricmp(cp, ".mod"))
setString(partial, sizeof(partial), "mod.");
else
setString(partial, sizeof(partial), "scr.");
addString(partial, sizeof(partial), frame[stack].script->name);
cp = (char *)strstr("::", partial);
if(cp)
*cp = 0;
addString(partial, sizeof(partial), id);
id = partial;
}
retry:
if(!isalnum(*id) && *id != '_')
{
logmissing(id, "invalid");
return NULL;
}
while(count < 64)
{
if(!id[count])
break;
if(!strchr("abcdefghijklmnopqrstuvwxyz01234567890._", tolower(id[count])))
{
logmissing(id, "invalid");
return NULL;
}
++count;
}
if(count == 64)
{
logmissing(id, "invalid");
return NULL;
}
if(size && isLocked(id))
size = 0;
syms = getSymbols(id);
if(!syms)
return NULL;
sym = deref(syms->find(id, size));
if(!sym && !strchr(id, '.'))
{
p = remapLocal();
if(!p)
return NULL;
snprintf(partial, sizeof(partial), "%s.%s", p, id);
id = partial;
goto retry;
}
return sym;
}
bool ScriptInterp::setNumber(const char *id, const char *value, unsigned dec)
{
Symbol *sym;
if(!dec)
dec = 11;
else
dec += 12;
sym = mapSymbol(id, dec);
if(!sym)
return false;
if(!value)
return true;
if(sym->type == symINITIAL)
sym->type = symNUMBER;
return commit(sym, value);
}
bool ScriptInterp::setSymbol(const char *id, const char *value, unsigned short size)
{
Symbol *sym;
if(!size)
size = symsize;
sym = mapSymbol(id, size);
if(!sym)
return false;
if(!value)
return true;
return commit(sym, value);
}
void ScriptInterp::trap(const char *trapid)
{
unsigned trap = cmd->getTrapId(trapid);
if(!trap)
{
if(!image)
return;
if(!stricmp(trapid, "first") || !stricmp(trapid, "top"))
{
frame[stack].tranflag = frame[stack].caseflag = false;
frame[stack].line = frame[stack].first;
return;
}
}
ScriptInterp::trap(trap);
}
void ScriptInterp::trap(unsigned id)
{
Line *trap = NULL;
unsigned base = frame[stack].base;
if(!image)
return;
// we can inherit traps at lower levels
for(;;)
{
trap = frame[stack].script->trap[id];
if(trap == frame[stack].first)
{
advance();
return;
}
if(!trap && !cmd->isInherited(id))
{
advance();
return;
}
if(trap || stack == base)
break;
pull();
}
// when doing a trap, always unwind loop or recursive stack frames
clearStack();
frame[stack].tranflag = frame[stack].caseflag = false;
frame[stack].line = frame[stack].first = trap;
if(!id)
{
if(!trap)
redirect("::exit");
exiting = true;
}
}
bool ScriptInterp::pull(void)
{
if(!stack)
{
error("stack-underflow");
return false;
}
if(frame[stack].local && frame[stack - 1].local != frame[stack].local)
delete frame[stack].local;
--stack;
return true;
}
bool ScriptInterp::push(void)
{
if(stack >= (SCRIPT_STACK_SIZE - 1))
{
error("stack-overflow");
return false;
}
frame[stack + 1] = frame[stack];
frame[stack + 1].caseflag = frame[stack + 1].tranflag = false;
++stack;
return true;
}
void ScriptInterp::clearStack(void)
{
unsigned indexes[SCRIPT_STACK_SIZE];
unsigned idx = 0, len = 0;
char values[SCRIPT_STACK_SIZE * 6];
while(stack)
{
if(frame[stack - 1].script != frame[stack].script)
break;
pull();
indexes[idx++] = frame[stack].index;
}
snprintf(values, 3, "%d", idx);
setSymbol("script.stack", values, 4);
values[1] = 0;
while(idx--)
{
snprintf(values + len, sizeof(values) - len, ",%d", indexes[idx]);
len = (unsigned)strlen(values);
}
setSymbol("script.index", values + 1, 3);
}
void ScriptInterp::skip(void)
{
frame[stack].line = frame[stack].line->next;
}
void ScriptInterp::advance(void)
{
if(updated)
return;
frame[stack].line = frame[stack].line->next;
updated = true;
}
void ScriptInterp::error(const char *errmsg)
{
char evtname[128];
setSymbol("script.error", errmsg);
snprintf(evtname, sizeof(evtname), "error:%s", errmsg);
if(scriptEvent(evtname))
return;
if((frame[stack].script->mask & 0x02) && frame[stack].script->trap[1])
{
trap(1);
return;
}
advance();
}
bool ScriptInterp::tryCatch(const char *id)
{
Name *scr;
char namebuf[160];
char *cp;
unsigned stk = frame[stack].base;
setString(namebuf, sizeof(namebuf), frame[stk].script->name);
cp = strstr(namebuf, "::");
if(cp)
*(cp + 2) = 0;
else
addString(namebuf, sizeof(namebuf), "::");
addString(namebuf, sizeof(namebuf), id);
scr = getScript(namebuf);
if(!scr || !push())
return false;
branching();
frame[stack].script = scr;
frame[stack].line = frame[stack].first = scr->first;
frame[stack].caseflag = frame[stack].tranflag = 0;
frame[stack].index = 0;
frame[stack].mask = getMask();
image->fastBranch(this);
return true;
}
void ScriptInterp::gotoEvent(NamedEvent *evt)
{
clearStack();
branching();
frame[stack].tranflag = frame[stack].caseflag = false;
frame[stack].line = frame[stack].first = evt->line;
image->fastBranch(this);
}
bool ScriptInterp::scriptEvent(const char *name, bool inhereted)
{
char evtname[128];
const char *savname = name;
NamedEvent *evt, *top = frame[stack].script->events;
unsigned base = frame[stack].base;
const char *chkname = name;
unsigned current = stack;
bool found = false;
#ifdef HAVE_REGEX_H
regex_t *regex;
#endif
retry:
evt = frame[current].script->events;
while(evt)
{
switch(evt->type)
{
case '@':
if(!stricmp(evt->name, chkname))
found = true;
break;
#ifdef HAVE_REGEX_H
case '~':
regex = new regex_t;
memset(regex, 0, sizeof(regex_t));
if(!regcomp(regex, evt->name, REG_ICASE|REG_NOSUB|REG_NEWLINE))
if(!regexec(regex, chkname, 0, NULL, 0))
found = true;
regfree(regex);
delete regex;
break;
#endif
}
if(found)
break;
evt = evt->next;
}
if(!evt && NULL != (chkname = strchr(chkname, ':')))
{
++chkname;
goto retry;
}
if(evt)
{
while(stack > current)
pull();
gotoEvent(evt);
return true;
}
while(current > base && frame[current].script->events == top && inhereted)
--current;
if(frame[current].script->events != top)
{
top = frame[current].script->events;
chkname = name;
goto retry;
}
if(*savname == '@')
++savname;
snprintf(evtname, sizeof(evtname), "-catch-%s", savname);
if(tryCatch(evtname))
return true;
return false;
}
void ScriptInterp::initRuntime(Name *scr)
{
MutexLock lock(*this);
while(stack)
pull();
frame[stack].script = scr;
frame[stack].line = frame[stack].first = frame[stack].script->first;
frame[stack].index = 0;
frame[stack].caseflag = frame[stack].tranflag = false;
frame[stack].decimal = 0;
frame[stack].base = 0;
frame[stack].mask = frame[stack].script->mask;
}
bool ScriptInterp::attach(ScriptCommand *cmdref, const char *scrname)
{
Name *scr;
char msg[65];
cmd = cmdref;
enterMutex();
purge();
cmd->enterMutex();
image = cmd->active;
if(!image)
{
cmd->leaveMutex();
leaveMutex();
return false;
}
scr = getScript(scrname);
if(!scr || scr->access != scrPUBLIC)
{
snprintf(msg, sizeof(msg), "%s: attach failed", scrname);
if(!image->getLast(msg))
{
image->setValue(msg, "missing");
cmd->errlog("missing", msg);
}
cmd->leaveMutex();
leaveMutex();
logerror("missing; attach failed", scrname);
snprintf(msg, sizeof(msg), "%s: attach failed", scrname);
return false;
}
++image->refcount;
cmd->leaveMutex();
attach(cmd, image, scr);
return true;
}
void ScriptInterp::attach(ScriptCommand *cmdref, ScriptImage *img, Name *scr)
{
const char *scrname = scr->name;
ScriptImage::InitialList *ilist;
ScriptBinder *mod;
Name *init;
Line *line;
stack = 0;
cmd = cmdref;
exiting = initialized = false;
session = this;
thread = NULL;
bool selected = false;
Symbol *sym;
image = img;
frame[stack].local = NULL;
for(tempidx = 0; tempidx < SCRIPT_TEMP_SPACE; ++tempidx)
temps[tempidx] = (char *)alloc(symsize + 1);
tempidx = 0;
ilist = image->ilist;
while(ilist)
{
setSymbol(ilist->name, ilist->value, ilist->size);
ilist = ilist->next;
}
sym = mapSymbol("script.authorize", 0);
if(sym)
sym->type = symTIMER;
setSymbol("script.home", scrname);
mod = ScriptBinder::first;
while(mod)
{
mod->attach(this);
mod = mod->next;
}
initialize();
init = image->index[SCRIPT_INDEX_SIZE];
leaveMutex();
while(init)
{
initRuntime(init);
while(step())
Thread::yield();
init = init->next;
}
initialized = true;
enterMutex();
initRuntime(scr);
mod = ScriptBinder::first;
while(mod && !selected)
{
selected = mod->select(this);
mod = mod->next;
}
if(selected)
goto finish;
if(fastStart)
{
image->fastBranch(this);
goto finish;
}
line = getLine();
if(!line)
goto finish;
if(!stricmp(line->cmd, "options"))
execute(line->scr.method);
finish:
leaveMutex();
}
void ScriptInterp::detach(void)
{
ScriptBinder *mod = ScriptBinder::first;
char scrname[65];
char *sp;
++sequence;
snprintf(scrname, sizeof(scrname), "%s", frame[0].script->name);
sp = strchr(scrname, ':');
if(sp)
*sp = 0;
if(!image)
return;
if(thread)
{
delete thread;
thread = NULL;
}
while(mod)
{
mod->detach(this);
mod = mod->next;
}
enterMutex();
cmd->enterMutex();
--image->refcount;
if(image)
if(!image->refcount && image != cmd->active)
delete image;
cmd->leaveMutex();
image = NULL;
while(stack)
pull();
purge();
leaveMutex();
}
bool ScriptInterp::step(void)
{
bool rtn = false;
Line *next, *line;
unsigned count = autoStepping;
if(!image)
return true;
enterMutex();
if(!frame[stack].line)
goto exit;
trans:
updated = false;
frame[stack].index = 0;
line = getLine();
next = line->next;
rtn = execute(line->scr.method);
release();
if(!rtn || !frame[stack].line)
goto exit;
if((frame[stack].tranflag && !trace))
{
count = 0;
goto trans;
}
if(count-- && frame[stack].line == next && !trace)
goto trans;
exit:
while(!frame[stack].line && stack)
{
if(frame[stack - 1].local == frame[stack].local)
break;
pull();
if(frame[stack].line)
advance();
}
if(!frame[stack].line)
{
if(initialized)
exit();
rtn = false;
}
else if(!rtn && thread)
{
release();
startThread();
}
else
release();
leaveMutex();
return rtn;
}
void ScriptInterp::ripple(void)
{
char namebuf[256];
char *label;
Name *scr = frame[stack].script;
Line *line;
const char *name = getValue(NULL);
snprintf(namebuf, sizeof(namebuf), "%s", name);
label = strchr(namebuf, ':');
if(!label)
{
label = namebuf;
goto find;
}
*(label++) = 0;
scr = getScript(namebuf);
if(!scr)
{
logmissing(name, "missing", "script");
error("label-missing");
return;
}
find:
if(!label || !*label)
{
line = scr->first;
goto done;
}
line = scr->first;
while(line)
{
if(!stricmp(line->cmd, "label"))
if(!stricmp(line->args[0], label))
break;
line = line->next;
}
if(!line)
{
logmissing(name, "missing", "script");
error("label-missing");
return;
}
done:
frame[stack].caseflag = false;
frame[stack].script = scr;
frame[stack].first = scr->first;
frame[stack].line = line;
frame[stack].index = 0;
frame[stack].mask = scr->mask;
updated = true;
}
bool ScriptInterp::redirect(bool evflag)
{
char namebuf[256];
const char *label = getValue(NULL);
char *ext;
size_t len;
bool pvt = true;
Name *scr = frame[stack].script;
bool shortflag = true;
unsigned base = frame[stack].base;
bool fun = false;
bool isfun = false;
unsigned long mask = frame[stack].line->mask & frame[stack].mask & cmd->imask;
if(!stricmp(frame[stack].line->cmd, "call"))
fun = true;
isfun = isFunction(scr);
if(!label)
{
logmissing(label, "missing", "script");
error("branch-failed");
return true;
}
if(*label == '&')
++label;
if(strstr(label, "::"))
shortflag = false;
len = strlen(label);
retry:
if(shortflag)
{
snprintf(namebuf, sizeof(namebuf), "%s", frame[stack].script->name);
ext = strstr(namebuf, "::");
if(ext)
*ext = 0;
len = strlen(namebuf);
snprintf(namebuf + len, sizeof(namebuf) - len, "::%s", label);
scr = getScript(namebuf);
if(scr)
{
pvt = false;
goto script;
}
shortflag = false;
goto retry;
}
else if(!strncmp(label, "::", 2))
{
pvt = false;
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(fun || isfun)
{
setString(namebuf, sizeof(namebuf), frame[stack].script->name);
ext = strstr(namebuf, "::");
if(ext)
setString(ext + 2, sizeof(namebuf) + namebuf - ext - 2, label);
else
{
addString(namebuf, sizeof(namebuf), "::");
addString(namebuf, sizeof(namebuf), label);
}
scr = getScript(namebuf);
if(scr)
{
pvt = false;
goto script;
}
}
scr = getScript(label);
script:
if(!scr)
{
logmissing(label, "missing", "script");
error("script-not-found");
return true;
}
if(pvt && isPrivate(scr))
{
logmissing(label, "access", "script");
error("script-private");
return true;
}
if(!isfun && !fun && isFunction(scr) && scr != frame[stack].script)
{
logmissing(label, "access", "script");
error("script-function");
return true;
}
if(!isFunction(scr))
isfun = false;
if(isfun && evflag)
clearStack();
else while(evflag && stack > base)
pull();
frame[stack].caseflag = false;
frame[stack].script = scr;
frame[stack].line = frame[stack].first = scr->first;
frame[stack].index = 0;
if(evflag && isfun && stack)
{
mask = frame[stack - 1].line->mask & frame[stack - 1].mask & cmd->imask;
frame[stack].mask = (mask | scr->mask);
}
else if(evflag)
frame[stack].mask = getMask();
else
frame[stack].mask = (mask | scr->mask);
updated = true;
return true;
}
bool ScriptInterp::redirect(const char *scriptname)
{
Name *scr;
char namebuf[128];
char *ext;
if(!strncmp(scriptname, "::", 2))
{
setString(namebuf, sizeof(namebuf), frame[stack].script->name);
ext = strstr(namebuf, "::");
if(ext)
*ext = 0;
addString(namebuf, sizeof(namebuf), scriptname);
}
else
setString(namebuf, sizeof(namebuf), scriptname);
scr = getScript(namebuf);
if(scr)
{
clearStack();
frame[stack].script = scr;
frame[stack].line = frame[stack].first = scr->first;
frame[stack].mask = getMask();
return true;
}
else
logmissing(namebuf, "missing", "script");
return false;
}
unsigned long ScriptInterp::getMask(void)
{
unsigned sp = frame[stack].base;
unsigned long mask = 0;
while(sp < stack)
{
mask |= (frame[sp].script->mask & frame[sp].line->mask & cmd->imask);
++sp;
}
mask |= frame[stack].script->mask;
return mask;
}
void ScriptInterp::enterThread(ScriptThread *thr)
{
}
bool ScriptInterp::eventThread(const char *evt, bool flag)
{
if(updated)
return false;
if(scriptEvent(evt, flag))
updated = true;
return updated;
}
void ScriptInterp::exitThread(const char *msg)
{
if(updated)
return;
if(msg)
error(msg);
else
advance();
updated = true;
}
void ScriptInterp::startThread(void)
{
thread->start();
}
void ScriptInterp::waitThread(void)
{
timeout_t timer = getTimeout();
if(!timer)
return;
Thread::sleep(timer);
enterMutex();
delete thread;
thread = NULL;
if(!updated)
error("timeout");
updated = true;
leaveMutex();
}
bool ScriptInterp::exit(void)
{
if(exiting)
return false;
exiting = true;
trap((unsigned)0);
if(frame[stack].line)
return true;
return redirect("::exit");
}
bool ScriptInterp::signal(const char *trapname)
{
unsigned long mask, cmask;
Line *line = getLine();
if(!image)
return true;
MutexLock lock(*this);
cmask = mask = cmd->getTrapMask(trapname);
mask &= line->mask;
mask &= frame[stack].mask;
if(frame[stack].line)
mask &= frame[stack].line->mask;
if(exiting)
mask &= ~1;
if(!mask)
return false;
// stop(mask);
trap(trapname);
branching();
image->fastBranch(this);
return true;
}
bool ScriptInterp::signal(unsigned id)
{
unsigned long mask, cmask;
if(!image)
return true;
if(!id && exiting)
return false;
MutexLock lock(*this);
if(id >= TRAP_BITS)
return false;
cmask = mask = cmd->getTrapMask(id);
mask &= frame[stack].mask;
if(frame[stack].line)
mask &= frame[stack].line->mask;
if(!mask)
return false;
// stop(mask);
trap(id);
branching();
image->fastBranch(this);
return true;
}
const char *ScriptInterp::hasOption(void)
{
for(;;)
{
if(frame[stack].index >= frame[stack].line->argc)
return NULL;
if(*frame[stack].line->args[frame[stack].index] != '=')
break;
frame[stack].index += 2;
}
return frame[stack].line->args[frame[stack].index];
}
const char *ScriptInterp::getOption(const char *def)
{
register const char *cp;
unsigned current;
get:
for(;;)
{
if(frame[stack].index >= frame[stack].line->argc)
return (char *)def;
cp = frame[stack].line->args[frame[stack].index];
if(stack && !stricmp(cp, "%*"))
goto expand;
if(*cp != '=')
break;
frame[stack].index += 2;
}
++frame[stack].index;
return cp;
expand:
current = stack;
while(stack && frame[stack].local == frame[current].local)
--stack;
if(frame[stack].local == frame[current].local)
{
stack = current;
goto get;
}
if(frame[stack].index >= frame[stack].line->argc)
frame[stack].index = 0;
cp = getOption(NULL);
if(!cp || frame[stack].index >= frame[stack].line->argc)
++frame[current].index;
stack = current;
if(!cp)
goto get;
return cp;
}
const char *ScriptInterp::getSymbol(const char *id)
{
const char *val = getExternal(id);
Symbol *sym;
if(val)
return val;
sym = mapSymbol(id);
if(!sym)
return NULL;
return extract(sym);
}
Script::Symbol *ScriptInterp::getSymbol(unsigned short size)
{
const char *id = getOption(NULL);
if(!id)
return NULL;
if(*id != '%' && *id != '&' && *id != '@')
return NULL;
return mapSymbol(id, size);
}
const char *ScriptInterp::getMember(void)
{
const char *cp = strchr(frame[stack].line->cmd, '.');
if(cp)
++cp;
return cp;
}
const char *ScriptInterp::getKeyoption(const char *kw)
{
unsigned idx = 0;
Line *line = frame[stack].line;
const char *opt;
while(idx < line->argc)
{
opt = line->args[idx++];
if(*opt == '=')
{
if(!strnicmp(kw, opt + 1, strlen(kw)))
return line->args[idx];
++idx;
}
}
return NULL;
}
const char *ScriptInterp::getKeyword(const char *kw)
{
unsigned idx = 0;
Line *line = frame[stack].line;
const char *opt;
while(idx < line->argc)
{
opt = line->args[idx++];
if(*opt == '=')
{
if(!strnicmp(kw, opt + 1, strlen(kw)))
return getContent(line->args[idx]);
++idx;
}
}
return NULL;
}
Script::Symbol *ScriptInterp::getKeysymbol(const char *kw, unsigned size)
{
const char *opt = getKeyoption(kw);
Symbol *sym;
if(!opt)
return NULL;
if(*opt != '&')
return NULL;
sym = mapSymbol(opt, size);
if(!sym)
logmissing(opt);
return deref(sym);
}
const char *ScriptInterp::getSymContent(const char *opt)
{
Symbol *sym;
if(!opt)
return NULL;
if(*opt != '&')
return getContent(opt);
sym = mapSymbol(++opt);
if(sym)
return extract(sym);
logmissing(opt);
return NULL;
}
const char *ScriptInterp::getContent(const char *opt)
{
Symbol *sym;
const char *val;
char *tmp;
Array *a;
ScriptProperty *p;
long v;
if(!opt)
return NULL;
if(*opt == '%' && !opt[1])
return opt;
if(*opt == '{')
return ++opt;
if(*opt == '#')
{
tmp = getTemp();
val = getExternal(++opt);
if(val)
{
snprintf(tmp, 11, "%ld", (long)strlen(val));
return tmp;
}
sym = mapSymbol(opt);
if(!sym)
{
logmissing(opt);
return NULL;
}
tmp = getTemp();
a = (Array *)&sym->data;
switch(sym->type)
{
case symBOOL:
switch(sym->data[0])
{
case '0':
case 'n':
case 'N':
case 'f':
case 'F':
tmp[0] = '0';
break;
default:
tmp[0] = '1';
break;
}
tmp[1] = 0;
return tmp;
case symARRAY:
snprintf(tmp, 11, "%d", a->tail);
return tmp;
case symCONST:
case symNORMAL:
snprintf(tmp, 11, "%ld", (long)strlen(sym->data));
return tmp;
case symCOUNTER:
snprintf(tmp, 11, "%ld", atol(sym->data));
return tmp;
case symTIMER:
if(!sym->data[0])
{
setString(tmp, 11, "999999999");
return tmp;
}
v = atol(extract(sym));
if(v < 0)
snprintf(tmp, 11, "%ld", -v);
else
setString(tmp, 11, "0");
return tmp;
case symINITIAL:
return "0";
case symPROPERTY:
opt = sym->data + sizeof(ScriptProperty *);
memcpy(&p, &sym->data, sizeof(p));
snprintf(tmp, 11, "%ld", p->getValue(opt));
return tmp;
case Script::symSTACK:
case Script::symFIFO:
if(a->tail >= a->head)
snprintf(tmp, 11, "%d", a->tail - a->head);
else
snprintf(tmp, 11, "%d", a->count - (a->tail - a->head));
return tmp;
default:
return NULL;
}
}
if(*opt != '%' && *opt != '@')
return opt;
if(*opt != '@')
{
++opt;
val = session->getExternal(opt);
if(val)
return val;
}
sym = mapSymbol(opt);
if(sym)
return extract(sym);
logmissing(opt);
return NULL;
}
const char *ScriptInterp::getValue(const char *def)
{
const char *opt = getOption(NULL);
if(!opt)
return def;
opt = getContent(opt);
if(!opt)
return def;
return opt;
}
void ScriptInterp::logerror(const char *msg, const char *scrname)
{
if(!scrname && frame[stack].script)
scrname = frame[stack].script->name;
if(scrname)
slog.error() << logname << ": " << scrname << ": " << msg << endl;
else
slog.error() << logname << ": " << msg << endl;
}
void ScriptInterp::logmissing(const char *sym, const char *reason, const char *group)
{
char msg[65];
if(*sym == '@' || *sym == '%' || *sym == '&')
++sym;
if(!frame[stack].line)
return;
slog.warn() << logname << ": " << frame[stack].script->filename
<< "(" << frame[stack].line->lnum << "): " << group << " "
<< sym << " " << reason << endl;
snprintf(msg, sizeof(msg), "%s(%d): %s %s",
frame[stack].script->filename, frame[stack].line->lnum, group, sym);
cmd->enterMutex();
if(image->getLast(msg))
{
cmd->leaveMutex();
return;
}
image->setValue(msg, reason);
if(!stricmp(reason, "undefined"))
reason = "missing";
cmd->errlog(reason, msg);
cmd->leaveMutex();
}
bool ScriptInterp::done(void)
{
if(!stack && !frame[stack].line && exiting)
return true;
return false;
}
bool ScriptInterp::catSymbol(const char *id, const char *value, unsigned short size)
{
Symbol *sym;
if(!id)
return false;
if(!value)
return true;
while(*id == '%' || *id == '&' || *id == '@')
++id;
if(!*id)
return false;
MutexLock local(*this);
if(strchr(id, '.') && session != this)
MutexLock sess(*session);
sym = mapSymbol(id, size);
if(!sym)
return false;
return append(sym, value);
}
bool ScriptInterp::putSymbol(const char *id, const char *value, unsigned short size)
{
Symbol *sym;
if(!id)
return false;
if(!value)
value = "";
while(*id == '&' || *id == '%' || *id == '@')
++id;
if(!*id)
return false;
MutexLock local(*this);
if(strchr(id, '.') && session != this)
MutexLock sess(*session);
sym = mapSymbol(id, size);
if(!sym)
return false;
return commit(sym, value);
}
bool ScriptInterp::getSymbol(const char *id, char *buffer, unsigned short size)
{
Symbol *sym;
const char *cp;
if(!id)
return false;
if(!buffer)
return false;
while(*id == '&' || *id == '%' || *id == '@')
++id;
if(!*id)
return false;
MutexLock local(*this);
if(strchr(id, '.') && session != this)
MutexLock sess(*session);
sym = mapSymbol(id);
if(!sym)
return false;
*buffer = 0;
cp = extract(sym);
if(!cp)
return false;
setString(buffer, size, cp);
return true;
}
timeout_t ScriptInterp::getTimeout(void)
{
if(!thread)
return 0;
return thread->getTimeout();
}
syntax highlighted by Code2HTML, v. 0.9.1