-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathEmbeddedInterpreter.cc
More file actions
394 lines (335 loc) · 13.8 KB
/
EmbeddedInterpreter.cc
File metadata and controls
394 lines (335 loc) · 13.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
#include "EmbeddedInterpreter.h"
#include "StaticBuffer.h"
#include "NullEmbeddedInterpreter.h"
#include "Plugin.h"
#include "Config.h"
#include "Hook.h"
#include "Pipe.h"
#include "Interpreter.h"
#include <dlfcn.h>
#include <unistd.h>
#if __GNUC__ >= 3
#include <locale> // isspace
#else
#include <ctype.h>
#endif
#define DIRT_LOCAL_LIBRARY_PATH "/usr/local/lib/dirt"
#define DIRT_LIBRARY_PATH "/usr/lib/dirt"
#define DEFAULT_INTERP ""
extern time_t current_time;
bool EmbeddedInterpreter::constboolfalse = false;
// Default interpreter if we don't manage to load anything
static NullEmbeddedInterpreter nullEmbeddedInterpreter;
EmbeddedInterpreter *embed_interp = &nullEmbeddedInterpreter;
static StackedInterpreter *stacked_interp;
static int interp_count = 0; // How many interpreters total do we have
void EmbeddedInterpreter::runCallouts() {
static int last_callout_time;
if(last_callout_time != current_time) {
last_callout_time = current_time;
hook.run(IDLE);
}
}
StackedInterpreter::StackedInterpreter(EmbeddedInterpreter *e1,EmbeddedInterpreter *e2)
: interpreters()
{
interpreters[""] = e1;
interpreters[e1->name()] = e1;
interpreters[e2->name()] = e2;
}
StackedInterpreter::~StackedInterpreter() {
for(hash_map<string,EmbeddedInterpreter*,hashstring_type >::iterator it = interpreters.begin(); it != interpreters.end(); it++) {
if(it->first == "") { // This interpreter is in the list twice.
interpreters.erase(it);
continue;
}
delete it->second;
interpreters.erase(it);
}
}
void StackedInterpreter::add(EmbeddedInterpreter *e) {
if(interpreters.find("") == interpreters.end()) {
interpreters[""] = e;
}
interpreters[e->name()] = e;
}
// Slight code duplication here, but can't do much about it since the functions called are different,
// unless I start messing around with some functors I guess
bool StackedInterpreter::run(const char* lang, const char *function, const char *arg, char *out, savedmatch* sm, bool& haserror) {
char buf[interpreters.size()][MAX_MUD_BUF];
int i = 0;
bool res = false;
bool embhaserror = haserror;
bool err_somewhere = false;
if(lang && strlen(lang) > 0) {
embhaserror = true; // tell the embedded interpreter that it should report errors to us.
res = interpreters[lang]->run(lang, function, arg, buf[i++], sm, embhaserror) || res; // || res has no effect since res is false.
if(haserror) err_somewhere |= embhaserror;
} else {
for(hash_map<string,EmbeddedInterpreter*,hashstring_type >::iterator it = interpreters.begin(); it != interpreters.end(); it++) {
if(it->first == DEFAULT_INTERP) continue; // skip the default one -- it's in the list twice
embhaserror = true; // tell the embedded interpreter that it should report errors to us.
res = it->second->run(lang, function, arg, buf[i], sm, embhaserror) || res;
// If there was an error in any interpreter, err_somewhere should be true
// haserror (as passed) indicates whether we should care if the interpreter fails.
if(haserror) err_somewhere |= embhaserror;
arg = buf[i++];
}
}
haserror = err_somewhere;
if (out) strcpy(out, buf[i-1]); // minus one?!?!?!?
if(interpreterPipe->have_data()) interpreterPipe->inputReady();
return res;
}
bool StackedInterpreter::load_file(const char* filename, bool suppress) {
bool res = false;
for(hash_map<string,EmbeddedInterpreter*,hashstring_type >::iterator it = interpreters.begin(); it != interpreters.end(); it++) {
if(it->first == DEFAULT_INTERP) continue;
res = it->second->load_file(filename, suppress) || res;
}
return res;
}
bool StackedInterpreter::eval(const char* lang, const char* expr, const char* arg,
char* out, savedmatch* sm) {
if(!lang) lang = DEFAULT_INTERP; // In case it's null.
bool retval = interpreters[lang]->eval(lang, expr, arg, out, sm);
if(interpreterPipe->have_data()) interpreterPipe->inputReady();
return retval;
}
void *StackedInterpreter::match_prepare(const char* pattern, const char* replacement) {
return interpreters[DEFAULT_INTERP]->match_prepare(pattern,replacement);
}
void* StackedInterpreter::substitute_prepare(const char* pattern, const char* replacement) {
return interpreters[DEFAULT_INTERP]->match_prepare(pattern,replacement);
}
bool StackedInterpreter::match(void *perlsub, const char *str, char * const & out) {
return interpreters[DEFAULT_INTERP]->match(perlsub, str, out);
}
void StackedInterpreter::set(const char *var, int value) {
for(hash_map<string,EmbeddedInterpreter*,hashstring_type >::iterator it = interpreters.begin(); it != interpreters.end(); it++) {
if(it->first == DEFAULT_INTERP) continue;
it->second->set(var, value);
}
}
void StackedInterpreter::set(const char *var, const char* value) {
for(hash_map<string,EmbeddedInterpreter*,hashstring_type >::iterator it = interpreters.begin(); it != interpreters.end(); it++) {
if(it->first == DEFAULT_INTERP) continue;
it->second->set(var, value);
}
}
int StackedInterpreter::get_int(const char* name) {
return interpreters[DEFAULT_INTERP]->get_int(name);
}
char* StackedInterpreter::get_string(const char*name) {
return interpreters[DEFAULT_INTERP]->get_string(name);
}
bool StackedInterpreter::run_quietly(const char* lang, const char* path, const char *arg, char *out, bool suppress_error) {
char buf[interpreters.size()][MAX_MUD_BUF];
int i = 0;
bool res = false;
if(lang && strlen(lang) > 0) {
res = interpreters[lang]->run_quietly(lang, path, arg, buf[i], suppress_error) || res;
arg = buf[i++];
} else {
for(hash_map<string,EmbeddedInterpreter*,hashstring_type >::iterator it = interpreters.begin(); it != interpreters.end(); it++) {
if(it->first == DEFAULT_INTERP) continue;
res = it->second->run_quietly(lang, path, arg, buf[i], suppress_error) || res;
arg = buf[i++];
}
}
if (out && res) strcpy(out, buf[i-1]);
if(interpreterPipe->have_data()) interpreterPipe->inputReady();
return res;
}
vector<Plugin*> Plugin::plugins;
Plugin::Plugin(const char *_filename, void *_handle) : filename(_filename), handle(_handle) {
}
Plugin::~Plugin() {
dlclose(handle);
}
// Unload all the scripts
void Plugin::done() {
for(vector<Plugin*>::iterator it = plugins.begin(); it != plugins.end(); it++)
if ((*it)->doneFunction) {
(*it)->doneFunction();
delete *it;
}
}
// Load shared object and update interpreter settings
Plugin * Plugin::loadPlugin(const char *filename, const char *args) {
char buf[1024];
void *handle;
// Try global path or home directory
snprintf(buf, sizeof(buf), "o/plugins/%s", filename); // If running from compile dir.
if(access(buf, R_OK) < 0) {
snprintf(buf, sizeof(buf), "%s/.dirt/plugins/%s", getenv("HOME"), filename);
if (access(buf, R_OK) < 0) {
snprintf(buf, sizeof(buf), "%s/plugins/%s", DIRT_LOCAL_LIBRARY_PATH, filename);
if (access(buf, R_OK) < 0) {
snprintf(buf, sizeof(buf), "%s/plugins/%s", DIRT_LIBRARY_PATH, filename);
if (access(buf, R_OK) < 0)
error ("Error loading %s: not found\n"
"I tried looking for and failed to find:\n"
" %s/.dirt/plugins/%s\n"
" %s/plugins/%s\n"
" %s/plugins/%s\n"
"If you installed the standard binary distribution, you probably\n"
"forgot to move the plugin files to one of the above places.\n"
, filename, getenv("HOME"), filename, DIRT_LOCAL_LIBRARY_PATH, filename, DIRT_LIBRARY_PATH, filename);
}
}
}
if (!(handle = dlopen(buf, RTLD_LAZY|RTLD_GLOBAL)))
error ("Error loading %s: %s\n", buf, dlerror());
Plugin *plugin = new Plugin(buf, handle);
plugins.push_back(plugin);
// Call the initFunction, it returns an error message in case of error
const char *load_result;
if (!(plugin->initFunction = (InitFunction*)dlsym(handle, "initFunction")))
error ("Error loading %s: modules does not have a initFunction\n", buf);
if ((load_result = plugin->initFunction(args)))
error ("Error loading %s: %s\n", buf, load_result);
// Display some information about the module if the module so wishes
plugin->versionFunction = (VersionFunction*)dlsym(handle, "versionFunction");
plugin->doneFunction = (DoneFunction*)dlsym(handle, "doneFunction");
// Now let's setup the hooks
if ((plugin->createInterpreterFunction = (CreateInterpreterFunction*)dlsym(handle, "createInterpreter"))) {
EmbeddedInterpreter *interp = plugin->createInterpreterFunction();
if (!interp)
error ("Module %s: error creating interpreter", buf);
else {
if (stacked_interp) // If we already have a stacked set of interpreters, add it there
stacked_interp->add(interp);
else if (interp_count == 1) {
stacked_interp = new StackedInterpreter(embed_interp, interp);
embed_interp = stacked_interp;
} else // this is the first interpreter
embed_interp = interp;
}
interp_count++;
}
return plugin;
}
const char* Plugin::getVersionInformation() {
if (versionFunction)
return versionFunction();
else
return NULL;
}
void Plugin::displayLoadedPlugins() {
if (plugins.size() == 0)
report("Modules loaded: none");
else {
report("Modules loaded: ");
for(vector<Plugin*>::iterator it = plugins.begin(); it != plugins.end(); it++) {
const char *info = (*it)->getVersionInformation();
report("%s: %s", (*it)->filename.c_str(), info ? info : "(no version information)");
}
}
}
// Load all plugins
void Plugin::loadPlugins(const char *plugins) {
char module_name[INPUT_SIZE];
char module_args[INPUT_SIZE];
const char *s = plugins;
char *out;
// module_name [args], module_name [args]...
for (;*s;) {
while (isspace(*s))
s++;
out = module_name;
while (*s && !isspace(*s) && *s != ',')
*out++ = *s++;
*out = NUL;
if (!module_name[0])
continue;
out = module_args;
if (*s == ' ') { // module args follow space
out++;
while(*s && *s != ',')
*out++ = *s++;
}
*out = NUL;
if (*s == ',')
s++;
loadPlugin(Sprintf("%s.so", module_name), module_args);
}
}
bool NullEmbeddedInterpreter::load_file(const char*, bool) {
return false;
}
bool NullEmbeddedInterpreter::run_quietly(const char*, const char*, const char*, char*, bool) {
return false;
}
const char *EmbeddedInterpreter::findFile(const char *filename, const char *suffix) {
// Add suffix if not there already
if (strlen(filename) < strlen(suffix) || strcmp(filename+strlen(filename)-strlen(suffix), suffix))
filename = Sprintf("%s%s", filename, suffix);
const char *full;
// Try first compared to the .dirt directory
if (filename[0] != '/') {
full = Sprintf("%s/.dirt/%s", getenv("HOME"), filename);
if (access(full, R_OK) == 0) {
return full;
}
// Globally installed files
full = Sprintf("%s/lib/dirt/%s", INSTALL_ROOT, filename);
if (access(full, R_OK) == 0) {
return full;
}
}
if (access(filename, R_OK) == 0)
return filename;
return NULL;
}
// Note: we don't want to use the second argument.
// When Interpreter is constructed, embed_interp points to a NullEmbeddedInterpreter.
// Then later on embed_interp is assigned a new value pointing to a StackedInterpreter.
// If we pass embed_interp to hook.add(...) when Interpreter is constructed, it will
// never see the interpreters!
bool EmbeddedInterpreter::command_load(string& str, void*, savedmatch* sm) {
OptionParser opt(str, "");
if(sm) sm->retval = true;
if(!opt.valid()) return true;
if(!embed_interp->load_file(opt.restStr().c_str())) {
int CmdChar = config->getOption(opt_commandcharacter);
report("%cload: Unable to load file: %s\n", CmdChar, opt.restStr().c_str());
}
return true;
}
bool EmbeddedInterpreter::command_run(string& str, void*, savedmatch* sm) {
bool retval;
OptionParser opt(str, "L:");
if(!opt.valid()) return true;
char out[MAX_MUD_BUF]; // FIXME
if(opt.argc() < 2) {
int CmdChar = config->getOption(opt_commandcharacter);
report_err("%crun: Please pass a function name to run!\n", CmdChar);
return true;
}
retval = embed_interp->run(opt.gotOpt('L')?opt['L'].c_str():NULL, opt.restStr().c_str(), NULL, out, sm);
if(sm) {
sm->retval = retval;
sm->data = out;
} else str = out;
return true;
}
bool EmbeddedInterpreter::command_eval(string& str, void*, savedmatch* sm) {
bool retval;
OptionParser opt(str, "L:rs");
if(!opt.valid()) return true;
char out[MAX_MUD_BUF];
// report("command_eval passing savedmatch '%s' to embed_interp->eval\n", sm->data.c_str());
retval = embed_interp->eval(opt.gotOpt('L')?opt['L'].c_str():NULL, opt.restStr().c_str(), NULL, out, sm);
if(opt.gotOpt('r')) {
int CmdChar = config->getOption(opt_commandcharacter);
report("%ceval result: %s\n", CmdChar, out);
}
if(sm) {
sm->retval = retval;
sm->data = out;
} else str = out;
string strout(out);
if(opt.gotOpt('s')) interpreter.add(strout);
return true;
}