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