-// BotInterp.C -*- C++ -*-
-// Copyright (c) 1998 Etienne BERNARD
-// Copyright (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "Utils.H"
-#include "Bot.H"
-#include "BotInterp.H"
-
-#ifdef USESCRIPTS
-
-#include <libguile.h>
-extern "C"
-{
-#include <libguile/regex-posix.h>
-}
-
-BotInterp::BotInterp(Bot *b, String fn)
- : bot(b), counter(0)
-{
- 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);
-}
-
-void
-BotInterp::ScriptLog(SCM throw_args)
-{
- scm_display_error_message(SCM_CADR (throw_args),
- SCM_CADDR (throw_args),
- logPort);
- scm_flush(logPort);
-}
-
-namespace
-{
- bool hptr_lt (const Hook* h, const Hook* h1)
- // Hook Pointer less than
- // Used to sort the Hooks list
- { return *h < *h1; }
-}
-
-bool
-BotInterp::AddHook(int hooktype, SCM regex, SCM function, int pri, bool fall,
- String name) {
- if (scm_string_p(regex) == SCM_BOOL_F)
- return false;
- String rx = Utils::to_upper (Utils::scm2str (regex));
- SCM r = scm_make_regexp(regex,
- scm_listify (gh_lookup("regexp/icase"),
- SCM_UNDEFINED));
- scm_gc_protect_object(r);
- scm_gc_protect_object(function);
- // First, we check if an hook doesn't exist yet
- std::list<Hook *>::iterator it = hooksMap[hooktype].begin();
- std::list<Hook *>::iterator it2 = hooksMap[hooktype].end();
-
- for ( ; it != it2; ++it)
- // It exists, we replace it.
- if ((*it)->regex_str == rx && (*it)->name == name) {
- scm_gc_unprotect_object((*it)->function);
- scm_gc_unprotect_object (r);
- (*it)->function = function;
- (*it)->priority = pri;
- (*it)->fallthru = fall;
- hooksMap[hooktype].sort (hptr_lt);
- return true;
- }
- // It does not exist, we create it
- hooksMap[hooktype].push_back (new Hook(hooktype, rx, r,
- function, pri, fall, name));
- hooksMap[hooktype].sort (hptr_lt);
- return true;
-}
-
-bool
-BotInterp::RunHooks(int hooktype, String match, SCM args)
-{
- SCM result;
- // We want to execute higher priority hooks first, so we start at
- // the end of the list instead of the beggining
- std::list<Hook *>::reverse_iterator it = hooksMap[hooktype].rbegin();
- std::list<Hook *>::reverse_iterator it2 = hooksMap[hooktype].rend();
- wrapper_data wd;
- wd.args = args;
- for ( ; it != it2; ++it) {
- if (scm_regexp_exec((*it)->regex, Utils::str2scm (match),
- SCM_UNDEFINED, SCM_UNDEFINED) != SCM_BOOL_F)
- {
- wd.func = (*it)->function;
- result = gh_catch(SCM_BOOL_T,
- (scm_t_catch_body) scm_apply_wrapper,
- static_cast<void *> (&wd),
- (scm_t_catch_handler) Interp::ErrorHandler, 0);
- if (! (*it)->fallthru)
- break;
- }
- }
- return true;
-}
-
-SCM
-BotInterp::AddTimer(int delay, SCM function)
-{
- int when = time(NULL) + delay;
- int c = ++counter;
- scm_gc_protect_object(function);
- Timer *t = new Timer(c, when, function);
- timersList.push_back(t);
- return scm_long2num (c);
-}
-
-bool
-BotInterp::DelTimer(SCM timer)
-{
- int count = scm_num2long(timer, SCM_ARG1, "BotInterp::DelTimer");
- std::list<Timer *>::iterator it = timersList.begin();
- std::list<Timer *>::iterator it2 = timersList.end();
-
- for ( ; it != it2; ++it) {
- if ((*it)->count == count) {
- scm_gc_unprotect_object((*it)->function);
- delete (*it);
- timersList.erase(it);
- return true;
- }
- }
- return false;
-}
-
-bool
-BotInterp::RunTimers(int now)
-{
- std::list<Timer *>::iterator it = timersList.begin();
- std::list<Timer *>::iterator it2 = timersList.end();
- std::list<Timer *>::iterator it3;
-
- struct wrapper_data wd;
- wd.args = scm_listify (SCM_UNDEFINED);
-
- while (it != it2) {
- if ((*it)->when <= now) {
- wd.func = (*it)->function;
- gh_catch(SCM_BOOL_T, (scm_t_catch_body) scm_apply_wrapper,
- (void *)&wd, (scm_t_catch_handler) Interp::ErrorHandler, 0);
- scm_gc_unprotect_object(wd.func);
- it3 = it;
- ++it3;
- delete (*it);
- timersList.erase(it);
- it = it3;
- } else {
- ++it;
- }
- }
- return true;
-}
-
-#endif
+// 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 <algorithm>
+
+#include "Utils.H"
+#include "Bot.H"
+#include "BotInterp.H"
+
+#ifdef USESCRIPTS
+
+#include <libguile.h>
+extern "C"
+{
+#include <libguile/regex-posix.h>
+}
+
+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)
+ {
+ std::cerr << "Matching...\n";
+ if (scm_regexp_exec((*it)->regex, Utils::str2scm (match),
+ SCM_UNDEFINED, SCM_UNDEFINED) != SCM_BOOL_F)
+ {
+ std::cerr << " Match " << (*it)->regex_str << std::endl;
+ bool fallthru_p = (*it)->fallthru;
+ wd.func = (*it)->function;
+ result = scm_internal_catch(SCM_BOOL_T,
+ (scm_t_catch_body)
+ Interp::LazyApplyWrapper,
+ static_cast<void *> (&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