// BotInterp.C -*- C++ -*- // Copyright (c) 1998 Etienne BERNARD // Copyright (C) 2002,2005,2008 Clinton Ebadi // 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., 51 Franklin Street, Fifth Floor, Boston, MA // 02110-1301, USA. #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "BotInterp.H" #include "Bot.H" #include "Interp.H" #include "Utils.H" #ifdef USESCRIPTS #include extern "C" { #include } bool Hook::operator< (const Hook & h) const { if (priority < h.priority) { return true; } else if (priority > h.priority) { return false; } else if (fallthru && h.fallthru) { return false; } else if (fallthru && !h.fallthru) { return false; } else if (!fallthru && h.fallthru) { return true; } else { // NOTE: This should never be reached return false; } } BotInterp::BotInterp(Bot *b, String fn) : bot(b), counter(0), hook_mutex (true), timer_mutex (true) { logPort = scm_open_file (Utils::str2scm (fn), Utils::str2scm ("a")); scm_gc_protect_object(logPort); } void BotInterp::Execute(String command) { Interp::Execute(bot, command); } void BotInterp::LoadScript(String filename) { Interp::LoadScript(bot, filename); } SCM BotInterp::ScriptLog() { return logPort; } namespace { struct HookFind { std::string rx; std::string name; HookFind (std::string r, std::string n) : rx (r), name (n) { } bool operator() (const Hook * hook) const { return hook->regex_str == rx && hook->name == name; } }; } bool BotInterp::AddHook(int hooktype, SCM regex, SCM function, int pri, bool fall, std::string name) { if (scm_string_p(regex) == SCM_BOOL_F) return false; BotLock hook_lock (hook_mutex); std::string rx = Utils::to_upper (Utils::scm2str (regex)); SCM r = scm_make_regexp (regex, scm_list_n (scm_variable_ref (scm_c_lookup ("regexp/icase")), SCM_UNDEFINED)); HookFind hook_find (rx, name); HookList& hook_list = hooks[hooktype]; scm_gc_protect_object(r); scm_gc_protect_object(function); HookList::iterator it = std::find_if (hook_list.begin (), hook_list.end (), hook_find); if (it != hook_list.end()) { Hook * found = *it; scm_gc_unprotect_object(found->function); scm_gc_unprotect_object (r); found->function = function; found->priority = pri; found->fallthru = fall; hook_list.erase (it); Utils::push_sorted (hook_list, found, hook_sort_p); return true; } else { Utils::push_sorted (hook_list, new Hook(hooktype, rx, r, function, pri, fall, name), hook_sort_p); return true; } } bool BotInterp::RunHooks(int hooktype, std::string match, SCM args) { BotLock hook_lock (hook_mutex); SCM result; wrapper_data wd; wd.args = args; // We want to execute higher priority hooks first, so we start at // the end of the list instead of the beginning for (HookList::reverse_iterator it = hooks[hooktype].rbegin(); it != hooks[hooktype].rend(); ++it) { if (scm_regexp_exec((*it)->regex, Utils::str2scm (match), SCM_UNDEFINED, SCM_UNDEFINED) != SCM_BOOL_F) { bool fallthru_p = (*it)->fallthru; wd.func = (*it)->function; result = scm_internal_catch(SCM_BOOL_T, (scm_t_catch_body) Interp::LazyApplyWrapper, static_cast (&wd), (scm_t_catch_handler) Interp::EmptyHandler, 0); if (!fallthru_p) break; } } return true; } SCM BotInterp::AddTimer(int delay, SCM function) { BotLock timer_lock (timer_mutex); int when = time(NULL) + delay; scm_gc_protect_object(function); Timer *timer = new Timer (++counter, when, function); Utils::push_sorted (timers, timer, timer_sort_p); return scm_from_int (counter); } bool BotInterp::DelTimer(SCM timer) { BotLock timer_lock (timer_mutex); int count = scm_to_int (timer); TimerList::iterator it = timers.begin(); TimerList::iterator end = timers.end(); for ( ; it != end; ++it) { if ((*it)->count == count) { scm_gc_unprotect_object((*it)->function); delete (*it); timers.erase(it); return true; } } return false; } bool BotInterp::RunTimers(int now) { BotLock timer_lock (timer_mutex); struct wrapper_data wd; wd.args = scm_list_n (SCM_UNDEFINED); while (!timers.empty ()) { // Keep a stack allocated copy of the front of the timer queue // just in case the timer is deleted while being executed (which // is very unlikely as the only place this could occur is if the // timer deleted itself) Timer current_timer = *timers.front () ; if (current_timer.when <= now) { wd.func = current_timer.function; scm_internal_catch (SCM_BOOL_T, (scm_t_catch_body) Interp::LazyApplyWrapper, (void *)&wd, (scm_t_catch_handler) Interp::EmptyHandler, 0); // The timer list may have been modified by the timer // callback; if it has in such a way that the first queue // item has changed (adding a timer in the past) then we // switch the slow path for deleting a timer if (current_timer.count == timers.front()->count) { scm_gc_unprotect_object (current_timer.function); delete timers.front (); timers.pop_front (); } else { DelTimer (scm_from_int (current_timer.count)); } } else { break; } } return true; } #endif