#ifdef HAVE_CONFIG_H # include #endif #ifdef HASRUBY #include #include #include #include #include #include #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