663 lines
15 KiB
C
663 lines
15 KiB
C
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#ifdef HASRUBY
|
|
#include <gtk/gtk.h>
|
|
#include <ruby.h>
|
|
#include <libgnomevfs/gnome-vfs-mime-utils.h>
|
|
#include <libgnomevfs/gnome-vfs.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#include "debug.h"
|
|
#include "gm-world.h"
|
|
#include "gm-app.h"
|
|
#include "gm-script.h"
|
|
#include "gm-scripts-dialog.h"
|
|
#include "gm-net.h"
|
|
#include "gm-support.h"
|
|
|
|
#define RB_CALLBACK(x) (VALUE (*)())(x)
|
|
|
|
static VALUE script_world_name(VALUE self);
|
|
VALUE script_world_create(world *wld);
|
|
void script_world_class_init();
|
|
void script_client_class_init();
|
|
|
|
static VALUE cWorld, cClient;
|
|
static gboolean didInit = FALSE;
|
|
static GList *funcs = NULL;
|
|
static GList *scriptFiles = NULL;
|
|
static gchar *fileInsp;
|
|
static GList *mondirs = NULL;
|
|
|
|
void gm_script_monitor_cb(GnomeVFSMonitorHandle *handle,
|
|
const gchar *monitor_uri, const gchar *info_uri,
|
|
GnomeVFSMonitorEventType event_type,
|
|
gpointer user_data) {
|
|
gchar *filename = gnome_vfs_get_local_path_from_uri(info_uri);
|
|
|
|
switch (event_type) {
|
|
case GNOME_VFS_MONITOR_EVENT_CHANGED:
|
|
gm_script_reload_file(filename);
|
|
break;
|
|
case GNOME_VFS_MONITOR_EVENT_DELETED:
|
|
gm_script_remove_file(filename);
|
|
break;
|
|
case GNOME_VFS_MONITOR_EVENT_CREATED:
|
|
gm_script_add_file(filename);
|
|
break;
|
|
default:
|
|
g_free(filename);
|
|
return;
|
|
break;
|
|
}
|
|
|
|
if_scripts_update();
|
|
g_free(filename);
|
|
}
|
|
|
|
void
|
|
script_destroy_function(script_function *fi) {
|
|
g_free(fi->name);
|
|
g_free(fi->fname);
|
|
g_free(fi->filename);
|
|
g_free(fi->description);
|
|
g_free(fi);
|
|
}
|
|
|
|
script_function *
|
|
script_find(gchar *name) {
|
|
GList *func;
|
|
script_function *f;
|
|
|
|
for (func = funcs; func; func = func->next) {
|
|
f = (script_function *)func->data;
|
|
|
|
if (strcasecmp(f->name, name) == 0) {
|
|
return f;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
gboolean
|
|
script_add(gchar *name, gchar *fname, gchar *description, gchar *filename) {
|
|
script_function *func;
|
|
gchar *basedir = PACKAGE_DATA_DIR "/" PACKAGE "/scripts";
|
|
|
|
if (script_find(name) == NULL) {
|
|
func = g_new(script_function, 1);
|
|
|
|
if (strncmp(filename, basedir, strlen(basedir)) == 0) {
|
|
func->type = ST_SHARE;
|
|
} else {
|
|
func->type = ST_USER;
|
|
}
|
|
|
|
func->name = g_strdup(name);
|
|
func->fname = g_strdup(fname);
|
|
func->filename = g_strdup(filename);
|
|
func->description = g_strdup(description);
|
|
|
|
funcs = g_list_append(funcs, func);
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
VALUE
|
|
register_func(int argc, VALUE *argv, VALUE self) {
|
|
char *name, *fname = NULL, *description = NULL;
|
|
gchar *msg;
|
|
|
|
if (argc > 1) {
|
|
name = rb_string_value_cstr(&argv[0]);
|
|
description = rb_string_value_cstr(&argv[1]);
|
|
|
|
if (argc == 2) {
|
|
fname = name;
|
|
} else {
|
|
fname = rb_string_value_cstr(&argv[2]);
|
|
}
|
|
|
|
if (script_add(name, fname, description, fileInsp)) {
|
|
msg = g_strdup_printf(_("Register function '%s' from '%s'"), name, fileInsp);
|
|
debug_msg(1, "register_func: adding script function %s from %s", name, fileInsp);
|
|
if_scripts_add(SCRIPTC_MSG, msg);
|
|
g_free(msg);
|
|
return Qtrue;
|
|
} else {
|
|
msg = g_strdup_printf(_("Script '%s' is already defined"), name);
|
|
debug_msg(1, "register_func: script function %s already defined!", name);
|
|
if_scripts_add(SCRIPTC_ERROR, msg);
|
|
g_free(msg);
|
|
return Qfalse;
|
|
}
|
|
} else {
|
|
return Qfalse;
|
|
}
|
|
}
|
|
|
|
VALUE
|
|
register_functions_wrap(VALUE arg) {
|
|
return rb_eval_string("register_functions");
|
|
}
|
|
|
|
void
|
|
script_define_world(VALUE *wld) {
|
|
rb_define_variable("$world", wld);
|
|
}
|
|
|
|
void
|
|
script_error() {
|
|
int c;
|
|
VALUE lasterr;
|
|
char *err;
|
|
gchar *msg;
|
|
VALUE ary;
|
|
|
|
if(!NIL_P(ruby_errinfo)) {
|
|
|
|
lasterr = rb_gv_get("$!");
|
|
err = RSTRING(rb_obj_as_string(lasterr))->ptr;
|
|
|
|
debug_msg(1, "script_error: Error while executing Ruby code: %s", err);
|
|
|
|
msg = g_strdup_printf(_("Error on executing: %s"), err);
|
|
if_scripts_add(SCRIPTC_ERROR, msg);
|
|
g_free(msg);
|
|
|
|
ary = rb_funcall(ruby_errinfo, rb_intern("backtrace"), 0);
|
|
debug_msg(1, "script_error: Ruby backtrace:");
|
|
if_scripts_add(SCRIPTC_ERROR, _("Ruby backtrace:"));
|
|
|
|
for (c = 0; c < RARRAY(ary)->len; c++) {
|
|
debug_msg(1, "script_error: \tfrom %s", RSTRING(RARRAY(ary)->ptr[c])->ptr);
|
|
|
|
msg = g_strdup_printf(_("\tfrom %s"), RSTRING(RARRAY(ary)->ptr[c])->ptr);
|
|
if_scripts_add(SCRIPTC_ERROR, msg);
|
|
g_free(msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
do_ruby(VALUE (*body)(), VALUE arg) {
|
|
int status;
|
|
|
|
rb_protect(body, arg, &status);
|
|
|
|
if (status != 0) {
|
|
script_error();
|
|
ruby_cleanup(status);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
VALUE
|
|
function_run(script_info *arg) {
|
|
VALUE ret;
|
|
gchar *argstr;
|
|
gchar *funcAll;
|
|
|
|
if (arg->argstr) {
|
|
argstr = str_escape(arg->argstr);
|
|
funcAll = g_strconcat(arg->name, "(\"", argstr, "\")", NULL);
|
|
g_free(argstr);
|
|
} else {
|
|
funcAll = g_strconcat(arg->name, "()", NULL);
|
|
}
|
|
|
|
ret = rb_eval_string(funcAll);
|
|
g_free(funcAll);
|
|
|
|
return ret;
|
|
}
|
|
|
|
gboolean
|
|
script_run(world *wld, script_function *f, gchar *argstr) {
|
|
VALUE rWld;
|
|
VALUE rClient;
|
|
gchar *msg;
|
|
script_info *info = g_new(script_info, 1);
|
|
|
|
info->name = g_strdup(f->fname);
|
|
|
|
if (argstr) {
|
|
info->argstr = g_strdup(argstr);
|
|
msg = g_strdup_printf(_("Run script '%s' from '%s' (%s)"), f->fname,
|
|
f->filename, argstr);
|
|
} else {
|
|
info->argstr = NULL;
|
|
msg = g_strdup_printf(_("Run script '%s' from '%s' ()"), f->fname,
|
|
f->filename);
|
|
}
|
|
|
|
if_scripts_add(SCRIPTC_RUN, msg);
|
|
g_free(msg);
|
|
|
|
do_ruby(RB_CALLBACK(rb_load_file), (VALUE) f->filename);
|
|
ruby_exec();
|
|
|
|
rWld = script_world_create(wld);
|
|
rb_define_variable("$world", &rWld);
|
|
|
|
rClient = rb_class_new_instance(0, NULL, cClient);
|
|
rb_define_variable("$client", &rClient);
|
|
|
|
do_ruby(RB_CALLBACK(function_run), (VALUE) info);
|
|
|
|
g_free(info->name);
|
|
|
|
if (info->argstr) {
|
|
g_free(info->argstr);
|
|
}
|
|
|
|
g_free(info);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GList *script_get_scripts() {
|
|
return funcs;
|
|
}
|
|
|
|
GList *script_get_files() {
|
|
return scriptFiles;
|
|
}
|
|
|
|
void
|
|
script_register_functions(gchar *filename) {
|
|
gchar *msg;
|
|
|
|
fileInsp = filename;
|
|
debug_msg(1, "script_register_functions: registering functions in %s", filename);
|
|
|
|
msg = g_strdup_printf(_("Registering functions from '%s'"), filename);
|
|
if_scripts_add(SCRIPTC_MSG, msg);
|
|
g_free(msg);
|
|
|
|
if (!do_ruby(RB_CALLBACK(rb_load_file), (VALUE) filename)) {
|
|
return;
|
|
}
|
|
|
|
ruby_exec();
|
|
|
|
rb_define_global_function("register_func", ®ister_func, -1);
|
|
|
|
do_ruby(RB_CALLBACK(register_functions_wrap), 0);
|
|
}
|
|
|
|
void
|
|
script_remove_file(const gchar *uri) {
|
|
GList *f, *l;
|
|
script_function *func;
|
|
gchar *msg;
|
|
|
|
l = g_list_copy(scriptFiles);
|
|
|
|
for (f = l; f; f = f->next) {
|
|
if (strcmp(f->data, uri) == 0) {
|
|
scriptFiles = g_list_remove(scriptFiles, f->data);
|
|
g_free(f->data);
|
|
}
|
|
}
|
|
|
|
g_list_free(l);
|
|
|
|
l = g_list_copy(funcs);
|
|
|
|
for (f = l; f; f = f->next) {
|
|
func = (script_function *)(f->data);
|
|
|
|
if (strcmp(func->filename, uri) == 0) {
|
|
msg = g_strdup_printf(_("Removing function `%s' from `%s'!"), func->name,
|
|
func->filename);
|
|
debug_msg(1, "script_remove_file: %s", msg);
|
|
if_scripts_add(SCRIPTC_MSG, msg);
|
|
g_free(msg);
|
|
funcs = g_list_remove(funcs, func);
|
|
script_destroy_function(func);
|
|
}
|
|
}
|
|
|
|
g_list_free(l);
|
|
}
|
|
|
|
gboolean
|
|
script_add_file(const gchar *uri) {
|
|
GList *f;
|
|
gchar *msg;
|
|
|
|
for (f = scriptFiles; f; f = f->next) {
|
|
if (strcmp(f->data, uri) == 0) {
|
|
msg = g_strdup_printf(_("File `%s' already added!"), uri);
|
|
debug_msg(1, "script_add_file: %s", msg);
|
|
if_scripts_add(SCRIPTC_MSG, msg);
|
|
g_free(msg);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
msg = g_strdup_printf(_("File `%s' added!"), uri);
|
|
debug_msg(1, "script_add_file: %s", msg);
|
|
if_scripts_add(SCRIPTC_MSG, msg);
|
|
g_free(msg);
|
|
scriptFiles = g_list_append(scriptFiles, strdup(uri));
|
|
script_register_functions((gchar *)uri);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
script_reload_file(const gchar *uri) {
|
|
script_remove_file(uri);
|
|
script_add_file(uri);
|
|
}
|
|
|
|
void
|
|
script_check_dir(gchar *dirname) {
|
|
gchar *filename;
|
|
gchar *file;
|
|
GDir *d;
|
|
GnomeVFSMonitorHandle *handle;
|
|
|
|
if (g_file_test(dirname, G_FILE_TEST_EXISTS) &&
|
|
g_file_test(dirname, G_FILE_TEST_IS_DIR)) {
|
|
|
|
if ((d = g_dir_open(dirname, 0, NULL))) {
|
|
while ((file = (gchar *)g_dir_read_name(d))) {
|
|
filename = g_strconcat(dirname, "/", file, NULL);
|
|
script_add_file(filename);
|
|
g_free(filename);
|
|
}
|
|
}
|
|
} else if (!g_file_test(dirname, G_FILE_TEST_EXISTS)) {
|
|
mkdir(dirname, 0750);
|
|
}
|
|
|
|
if (g_file_test(dirname, G_FILE_TEST_EXISTS)) {
|
|
|
|
gnome_vfs_monitor_add(&handle, dirname, GNOME_VFS_MONITOR_DIRECTORY,
|
|
script_monitor_cb, NULL);
|
|
|
|
mondirs = g_list_append(mondirs, handle);
|
|
}
|
|
}
|
|
|
|
void
|
|
script_init() {
|
|
gchar *path = g_strconcat(main_get_app_path(), "/scripts", NULL);
|
|
|
|
ruby_init();
|
|
|
|
if (!didInit) {
|
|
script_world_class_init();
|
|
script_client_class_init();
|
|
}
|
|
|
|
script_check_dir(path);
|
|
script_check_dir(PACKAGE_DATA_DIR "/" PACKAGE "/scripts");
|
|
|
|
g_free(path);
|
|
|
|
didInit = TRUE;
|
|
}
|
|
|
|
void
|
|
script_fini() {
|
|
GList *f;
|
|
script_function *fi;
|
|
gchar *filename;
|
|
|
|
for (f = funcs; f; f = f->next) {
|
|
fi = (script_function *)f->data;
|
|
script_destroy_function(fi);
|
|
}
|
|
|
|
g_list_free(funcs);
|
|
funcs = NULL;
|
|
|
|
for (f = scriptFiles; f; f = f->next) {
|
|
filename = (gchar *)(f->data);
|
|
g_free(filename);
|
|
}
|
|
|
|
g_list_free(scriptFiles);
|
|
scriptFiles = NULL;
|
|
|
|
for (f = mondirs; f; f = f->next) {
|
|
gnome_vfs_monitor_cancel((GnomeVFSMonitorHandle *)f->data);
|
|
}
|
|
|
|
g_list_free(mondirs);
|
|
mondirs = NULL;
|
|
|
|
ruby_finalize();
|
|
}
|
|
|
|
void
|
|
script_flush_and_reload() {
|
|
if_scripts_add(SCRIPTC_MSG, _("Flushing scripts..."));
|
|
script_fini();
|
|
|
|
if_scripts_add(SCRIPTC_MSG, _("Reloading scripts..."));
|
|
script_init();
|
|
}
|
|
|
|
/* CLASS FUNCTIONS */
|
|
|
|
static VALUE
|
|
script_world_name(VALUE self) {
|
|
world *wld;
|
|
|
|
Data_Get_Struct(self, world, wld);
|
|
|
|
return rb_str_new2(options_get_str(wld->settings, "name"));
|
|
}
|
|
|
|
static VALUE
|
|
script_world_host(VALUE self) {
|
|
world *wld;
|
|
|
|
Data_Get_Struct(self, world, wld);
|
|
|
|
return rb_str_new2(options_get_str(wld->settings, "host"));
|
|
}
|
|
|
|
static VALUE
|
|
script_world_port(VALUE self) {
|
|
world *wld;
|
|
|
|
Data_Get_Struct(self, world, wld);
|
|
|
|
return rb_str_new2(options_get_str(wld->settings, "port"));
|
|
}
|
|
|
|
static VALUE
|
|
script_world_writeln(VALUE self, VALUE str) {
|
|
world *wld;
|
|
gchar *strVal;
|
|
|
|
Data_Get_Struct(self, world, wld);
|
|
strVal = rb_string_value_cstr(&str);
|
|
|
|
world_writeln(wld, strVal);
|
|
|
|
return Qnil;
|
|
}
|
|
|
|
static VALUE
|
|
script_world_sendln(VALUE self, VALUE str) {
|
|
world *wld;
|
|
gchar *strVal;
|
|
|
|
Data_Get_Struct(self, world, wld);
|
|
strVal = rb_string_value_cstr(&str);
|
|
|
|
net_send_line(wld, (unsigned char *)strVal);
|
|
|
|
return Qnil;
|
|
}
|
|
|
|
static VALUE
|
|
script_world_input(VALUE self, VALUE str) {
|
|
world *wld;
|
|
gchar *strVal;
|
|
|
|
Data_Get_Struct(self, world, wld);
|
|
strVal = rb_string_value_cstr(&str);
|
|
|
|
world_process_input(wld, strVal);
|
|
|
|
return Qnil;
|
|
}
|
|
|
|
static VALUE
|
|
script_world_loaded(VALUE self) {
|
|
world *wld;
|
|
|
|
Data_Get_Struct(self, world, wld);
|
|
|
|
if (wld->loaded) {
|
|
return Qtrue;
|
|
} else {
|
|
return Qfalse;
|
|
}
|
|
}
|
|
|
|
static VALUE
|
|
script_world_connected(VALUE self) {
|
|
world *wld;
|
|
|
|
Data_Get_Struct(self, world, wld);
|
|
|
|
if (wld->net.state == NET_CONNECTED) {
|
|
return Qtrue;
|
|
} else {
|
|
return Qfalse;
|
|
}
|
|
}
|
|
|
|
static VALUE
|
|
script_world_quit(VALUE self) {
|
|
world *wld;
|
|
|
|
Data_Get_Struct(self, world, wld);
|
|
|
|
world_unload(wld);
|
|
return Qnil;
|
|
}
|
|
|
|
static VALUE
|
|
script_world_connect(int argc, VALUE *argv, VALUE self) {
|
|
world *wld;
|
|
gchar *strHost, *strPort;
|
|
|
|
Data_Get_Struct(self, world, wld);
|
|
|
|
if (argc == 0) {
|
|
strHost = options_get_str(wld->settings, "host");
|
|
} else {
|
|
strHost = rb_string_value_cstr(&(argv[0]));
|
|
}
|
|
|
|
if (argc == 0 || argc == 1) {
|
|
strPort = options_get_str(wld->settings, "port");
|
|
} else {
|
|
strPort = rb_string_value_cstr(&(argv[1]));
|
|
}
|
|
|
|
net_connect_to(wld, strHost, strPort);
|
|
|
|
return Qnil;
|
|
}
|
|
|
|
static VALUE
|
|
script_world_disconnect(VALUE self) {
|
|
world *wld;
|
|
|
|
Data_Get_Struct(self, world, wld);
|
|
|
|
net_do_disconnect(wld);
|
|
return Qnil;
|
|
}
|
|
|
|
void
|
|
script_world_class_init() {
|
|
cWorld = rb_define_class("World", rb_cObject);
|
|
|
|
rb_define_method(cWorld, "name", script_world_name, 0);
|
|
rb_define_method(cWorld, "host", script_world_host, 0);
|
|
rb_define_method(cWorld, "port", script_world_port, 0);
|
|
|
|
rb_define_method(cWorld, "writeln", script_world_writeln, 1);
|
|
rb_define_method(cWorld, "println", script_world_writeln, 1);
|
|
rb_define_method(cWorld, "sendln", script_world_sendln, 1);
|
|
rb_define_method(cWorld, "input", script_world_input, 1);
|
|
rb_define_method(cWorld, "quit", script_world_quit, 0);
|
|
rb_define_method(cWorld, "connect", script_world_connect, -1);
|
|
rb_define_method(cWorld, "disconnect", script_world_disconnect, 0);
|
|
rb_define_method(cWorld, "loaded?", script_world_loaded, 0);
|
|
rb_define_method(cWorld, "connected?", script_world_connected, 0);
|
|
}
|
|
|
|
VALUE
|
|
script_world_create(world *wld) {
|
|
VALUE tData = Data_Wrap_Struct(cWorld, 0, 0, wld);
|
|
|
|
return tData;
|
|
}
|
|
|
|
|
|
static VALUE
|
|
script_client_version(VALUE self) {
|
|
return rb_str_new2(VERSION);
|
|
}
|
|
|
|
static VALUE
|
|
script_client_worlds(VALUE self) {
|
|
GList *wld;
|
|
VALUE ar = rb_ary_new();
|
|
VALUE oWld;
|
|
|
|
for (wld = world_get_worlds(); wld; wld = wld->next) {
|
|
oWld = script_world_create((world *)(wld->data));
|
|
rb_ary_push(ar, oWld);
|
|
}
|
|
|
|
return ar;
|
|
}
|
|
|
|
static VALUE
|
|
script_client_open(VALUE self, VALUE str) {
|
|
world *wld;
|
|
gchar *strVal;
|
|
|
|
strVal = rb_string_value_cstr(&str);
|
|
|
|
wld = world_get_by_name(strVal);
|
|
|
|
if (wld == NULL) {
|
|
return Qfalse;
|
|
} else {
|
|
world_load(wld);
|
|
return Qtrue;
|
|
}
|
|
}
|
|
|
|
void
|
|
script_client_class_init() {
|
|
cClient = rb_define_class("Client", rb_cObject);
|
|
|
|
rb_define_method(cClient, "version", script_client_version, 0);
|
|
rb_define_method(cClient, "worlds", script_client_worlds, 0);
|
|
rb_define_method(cClient, "open", script_client_open, 1);
|
|
}
|
|
#endif
|