6d0349db50a6fdd9ab65dc19b2efa9d415fc248f
[clinton/bobotpp.git] / source / BotInterp.C
1 // BotInterp.C -*- C++ -*-
2 // Copyright (c) 1998 Etienne BERNARD
3 // Copyright (C) 2002,2005,2008 Clinton Ebadi
4
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 // 02110-1301, USA.
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <algorithm>
25
26 #include "BotInterp.H"
27
28 #include "Bot.H"
29 #include "Interp.H"
30 #include "Utils.H"
31
32
33 #ifdef USESCRIPTS
34
35 #include <libguile.h>
36 extern "C"
37 {
38 #include <libguile/regex-posix.h>
39 }
40
41 bool
42 Hook::operator< (const Hook & h) const
43 {
44 if (priority < h.priority)
45 {
46 return true;
47 }
48 else if (priority > h.priority)
49 {
50 return false;
51 }
52 else if (fallthru && h.fallthru)
53 {
54 return false;
55 }
56 else if (fallthru && !h.fallthru)
57 {
58 return false;
59 }
60 else if (!fallthru && h.fallthru)
61 {
62 return true;
63 }
64 else
65 {
66 // NOTE: This should never be reached
67 return false;
68 }
69 }
70
71 BotInterp::BotInterp(Bot *b, String fn)
72 : bot(b), counter(0),
73 hook_mutex (true), timer_mutex (true)
74 {
75 logPort = scm_open_file (Utils::str2scm (fn),
76 Utils::str2scm ("a"));
77
78 scm_gc_protect_object(logPort);
79 }
80
81 void
82 BotInterp::Execute(String command)
83 {
84 Interp::Execute(bot, command);
85 }
86
87 void
88 BotInterp::LoadScript(String filename)
89 {
90 Interp::LoadScript(bot, filename);
91 }
92
93 SCM
94 BotInterp::ScriptLog()
95 {
96 return logPort;
97 }
98
99 namespace
100 {
101 struct HookFind
102 {
103 std::string rx;
104 std::string name;
105
106 HookFind (std::string r, std::string n)
107 : rx (r), name (n)
108 { }
109
110 bool operator() (const Hook * hook) const
111 { return hook->regex_str == rx && hook->name == name; }
112 };
113 }
114
115 bool
116 BotInterp::AddHook(int hooktype, SCM regex, SCM function, int pri, bool fall,
117 std::string name)
118 {
119 if (scm_string_p(regex) == SCM_BOOL_F)
120 return false;
121
122 BotLock hook_lock (hook_mutex);
123 std::string rx = Utils::to_upper (Utils::scm2str (regex));
124 SCM r = scm_make_regexp (regex,
125 scm_list_n (scm_variable_ref (scm_c_lookup ("regexp/icase")),
126 SCM_UNDEFINED));
127 HookFind hook_find (rx, name);
128 HookList& hook_list = hooks[hooktype];
129
130 scm_gc_protect_object(r);
131 scm_gc_protect_object(function);
132
133 HookList::iterator it = std::find_if (hook_list.begin (),
134 hook_list.end (),
135 hook_find);
136
137 if (it != hook_list.end())
138 {
139 Hook * found = *it;
140
141 scm_gc_unprotect_object(found->function);
142 scm_gc_unprotect_object (r);
143
144 found->function = function;
145 found->priority = pri;
146 found->fallthru = fall;
147
148 hook_list.erase (it);
149 Utils::push_sorted (hook_list, found, hook_sort_p);
150
151 return true;
152 }
153 else
154 {
155 Utils::push_sorted (hook_list,
156 new Hook(hooktype, rx, r, function, pri, fall, name),
157 hook_sort_p);
158
159 return true;
160 }
161 }
162
163 bool
164 BotInterp::RunHooks(int hooktype, std::string match, SCM args)
165 {
166 BotLock hook_lock (hook_mutex);
167
168 SCM result;
169 wrapper_data wd;
170 wd.args = args;
171
172 // We want to execute higher priority hooks first, so we start at
173 // the end of the list instead of the beginning
174
175 for (HookList::reverse_iterator it = hooks[hooktype].rbegin();
176 it != hooks[hooktype].rend();
177 ++it)
178 {
179 std::cerr << "Matching...\n";
180 if (scm_regexp_exec((*it)->regex, Utils::str2scm (match),
181 SCM_UNDEFINED, SCM_UNDEFINED) != SCM_BOOL_F)
182 {
183 std::cerr << " Match " << (*it)->regex_str << std::endl;
184 bool fallthru_p = (*it)->fallthru;
185 wd.func = (*it)->function;
186 result = scm_internal_catch(SCM_BOOL_T,
187 (scm_t_catch_body)
188 Interp::LazyApplyWrapper,
189 static_cast<void *> (&wd),
190 (scm_t_catch_handler) Interp::EmptyHandler, 0);
191 if (!fallthru_p)
192 break;
193 }
194 }
195
196 return true;
197 }
198
199 SCM
200 BotInterp::AddTimer(int delay, SCM function)
201 {
202 BotLock timer_lock (timer_mutex);
203 int when = time(NULL) + delay;
204
205 scm_gc_protect_object(function);
206
207 Timer *timer = new Timer (++counter, when, function);
208 Utils::push_sorted (timers, timer, timer_sort_p);
209
210 return scm_from_int (counter);
211
212 }
213
214 bool
215 BotInterp::DelTimer(SCM timer)
216 {
217 BotLock timer_lock (timer_mutex);
218
219 int count = scm_to_int (timer);
220 TimerList::iterator it = timers.begin();
221 TimerList::iterator end = timers.end();
222
223 for ( ; it != end; ++it)
224 {
225 if ((*it)->count == count)
226 {
227 scm_gc_unprotect_object((*it)->function);
228 delete (*it);
229 timers.erase(it);
230
231 return true;
232 }
233 }
234
235 return false;
236 }
237
238 bool
239 BotInterp::RunTimers(int now)
240 {
241 BotLock timer_lock (timer_mutex);
242 struct wrapper_data wd;
243 wd.args = scm_list_n (SCM_UNDEFINED);
244
245 while (!timers.empty ())
246 {
247 // Keep a stack allocated copy of the front of the timer queue
248 // just in case the timer is deleted while being executed (which
249 // is very unlikely as the only place this could occur is if the
250 // timer deleted itself)
251 Timer current_timer = *timers.front () ;
252
253 if (current_timer.when <= now)
254 {
255 wd.func = current_timer.function;
256
257 scm_internal_catch (SCM_BOOL_T,
258 (scm_t_catch_body) Interp::LazyApplyWrapper,
259 (void *)&wd,
260 (scm_t_catch_handler) Interp::EmptyHandler,
261 0);
262
263 // The timer list may have been modified by the timer
264 // callback; if it has in such a way that the first queue
265 // item has changed (adding a timer in the past) then we
266 // switch the slow path for deleting a timer
267 if (current_timer.count == timers.front()->count)
268 {
269 scm_gc_unprotect_object (current_timer.function);
270 delete timers.front ();
271 timers.pop_front ();
272 }
273 else
274 {
275 DelTimer (scm_from_int (current_timer.count));
276 }
277 }
278 else
279 {
280 break;
281 }
282 }
283
284 return true;
285 }
286
287 #endif