changeset 3563:e120097bbd72

[gaim-migrate @ 3658] I made my perl script unloading not suck (as much). Now you may port your perl scripts--use gaim.pl and PERL-HOWTO as references. committer: Tailor Script <tailor@pidgin.im>
author Sean Egan <seanegan@gmail.com>
date Sat, 28 Sep 2002 08:08:14 +0000
parents de3bc24fff02
children 83a54877f1ae
files plugins/PERL-HOWTO plugins/gaim.pl src/core.h src/gaimrc.c src/module.c src/perl.c src/prefs.c
diffstat 7 files changed, 162 insertions(+), 110 deletions(-) [+]
line wrap: on
line diff
--- a/plugins/PERL-HOWTO	Sat Sep 28 03:48:28 2002 +0000
+++ b/plugins/PERL-HOWTO	Sat Sep 28 08:08:14 2002 +0000
@@ -42,7 +42,15 @@
 	Just like X-Chat. This is the first function your script should call.
 	shutdownroutine is a function that will be called when the script
 	gets unloaded (like when gaim gets closed). This function returns
-	gaim's version number.
+	gaim's version number.  This function MUST use the same Name and Version
+	given in description()--the plugin won't work otherwise.  This returns a
+	handle--you want to hold on to this.
+
+	The handle is what Gaim will use to distinguish your script from any others
+	running.  It's actually a string--the path to the script, but you'll probably
+	never need to know that.  As long as you just hold on to it and don't change it
+	everything should work fine.  You need it for GAIM::add_event_handler and 
+	GAIM::add_timeout_handler.
 
 GAIM::get_info(integer, ...)
 	This function returns different information based on the integer passed
@@ -125,11 +133,12 @@
 GAIM::print_to_chat(index, room, what)
 	Room is actually an int. Read SIGNALS to find out why.
 
-GAIM::add_event_handler(event, function)
+GAIM::add_event_handler(handle, event, function)
 	This is the most important of them all. This is basically exactly like
-	gaim_signal_connect for plugins. You pass which event you want to connect to
-	(a string with the same name as the events for plugins, see SIGNALS), and a
-	string with the name of the function you want called. Simple enough?
+	gaim_signal_connect for plugins. You pass the handle returned by GAIM::register,
+	which event you want to connect to (a string with the same name as the events for 
+	plugins, see SIGNALS), and a string with the name of the function you want called. 
+	Simple enough?
 
 	When this is triggered, the arguments will be passed in @_ and are broken
 	into a list. This is different from all previous versions of Gaim, where you
@@ -150,11 +159,11 @@
 	calls "function" as its handler.  The event handler must have been
 	previously added with GAIM::add_event_handler.
 
-GAIM::add_timeout_handler(integer, function, args)
+GAIM::add_timeout_handler(handle, integer, function, args)
 	This calls function after integer number of seconds. It only calls function
 	once, so if you want to keep calling function, keep readding the handler.
 	Args is a string that you'd like to have passed to your timeout handler; it's
-	optional.
+	optional.  Handle is the handle returned by GAIM::register--it is not optional.
 
 GAIM::play_sound(int sound)
 	Plays a sound using whatever method the user has selected. The argument is
--- a/plugins/gaim.pl	Sat Sep 28 03:48:28 2002 +0000
+++ b/plugins/gaim.pl	Sat Sep 28 08:08:14 2002 +0000
@@ -1,5 +1,12 @@
-GAIM::register("Example", "1.0", "goodbye", "");
+sub description {
+        my($a, $b, $c, $d, $e, $f) = @_;
+        ("Example", "1.0", "An example Gaim perl script that does nothing particularly useful:\n\t-Show a dialog on load\n\t-Set user idle for 6,000 seconds\n\t-Greets people signing on with \"Hello\"\n\t-Informs you when script has been loaded for one minute.", "Eric Warmenhoven &lt;eric\@warmenhoven.org>", "http://gaim.sf.net", "/dev/null");
+}
 
+$handle = GAIM::register("Example", "1.0", "goodbye", "");
+
+GAIM::print("Perl Says", "Handle $handle");
+		
 $ver = GAIM::get_info(0);
 @ids = GAIM::get_info(1);
 
@@ -10,12 +17,11 @@
 	$msg .= "\n$nam using $pro";
 }
 
-GAIM::print("Perl Says", $msg);
 
 GAIM::command("idle", 6000);
 
-GAIM::add_event_handler("event_buddy_signon", "echo_reply");
-GAIM::add_timeout_handler(60, "notify");
+GAIM::add_event_handler($handle, "event_buddy_signon", "echo_reply");
+GAIM::add_timeout_handler($handle, 60, "notify");
 
 sub echo_reply {
 	$index = $_[0];
@@ -31,8 +37,3 @@
 	GAIM::print("You Bastard!", "You killed Kenny!");
 }
 
-sub description {
-	my($a, $b, $c, $d, $e, $f) = @_;
-		("Example", "1.0", "An example Gaim perl script that does nothing particularly useful:\n\t-Show a dialog on load\n\t-Set user idle for 6,000 seconds\n\t-Greets people signing on with \"Hello\"\n\t-Informs you when script has been loaded for one minute.", "Eric Warmenhoven &lt;eric\@warmenhoven.org>", "http://gaim.sf.net", "/dev/null");
-		}
-		
--- a/src/core.h	Sat Sep 28 03:48:28 2002 +0000
+++ b/src/core.h	Sat Sep 28 08:08:14 2002 +0000
@@ -131,6 +131,7 @@
 	void *handle;
 	gchar path[128];
 	struct gaim_plugin_description desc;
+	gchar error[128];
 };
 
 #ifdef GAIM_PLUGINS
--- a/src/gaimrc.c	Sat Sep 28 03:48:28 2002 +0000
+++ b/src/gaimrc.c	Sat Sep 28 08:08:14 2002 +0000
@@ -400,8 +400,8 @@
 
 		p = (struct gaim_plugin *)pl->data;
 
-		path = escape_text2(g_module_name(p->handle));
-
+		path = escape_text2(p->path);
+			
 		fprintf(f, "\tplugin { %s }\n", path);
 
 		free(path);
--- a/src/module.c	Sat Sep 28 03:48:28 2002 +0000
+++ b/src/module.c	Sat Sep 28 08:08:14 2002 +0000
@@ -136,7 +136,7 @@
 #endif
 #ifdef USE_PERL
 				if (is_so_file(file, ".pl")) {
-					path = g_build_filename(LIBDIR, file, NULL);
+					path = g_build_filename(probedirs[l], file, NULL);
 					plug = probe_perl(path);
 					if (plug) 
 						probed_plugins = g_list_append(probed_plugins, plug);
@@ -159,7 +159,7 @@
 	GList *c = plugins;
 	GList *p = probed_plugins;
 	char *(*gaim_plugin_init)(GModule *);
-	char *error, *retval, *tmp;
+	char *error, *retval;
 	gboolean newplug = FALSE;
 
 	if (!g_module_supported())
@@ -167,6 +167,10 @@
 	if (!filename || !strlen(filename))
 		return NULL;
 
+	if (is_so_file(filename, ".pl")) {
+		return perl_load_file(filename);
+	}
+
 	while (filename && p) {
 		plug = (struct gaim_plugin *)p->data;
 		if (!strcmp(filename, plug->path)) 
@@ -190,21 +194,18 @@
 	if (!plug->handle) {
 		error = (char *)g_module_error();
 		plug->handle = NULL;
-		tmp = plug->desc.description;
-		plug->desc.description = g_strdup_printf("<span weight=\"bold\" foreground=\"red\">%s</span>\n\n%s", error, tmp);
-		g_free(tmp);
+		g_snprintf(plug->error, sizeof(plug->error), error);
 		return NULL;
 	}
 	
 	if (!g_module_symbol(plug->handle, "gaim_plugin_init", (gpointer *)&gaim_plugin_init)) {
 		g_module_close(plug->handle);
 		plug->handle = NULL;
-		tmp = plug->desc.description;
-		plug->desc.description = g_strdup_printf("<span foreground=\"red\">%s</span>\n\n%s", g_module_error(), tmp);
-		g_free(tmp);
+		g_snprintf(plug->error, sizeof(plug->error), error);
 		return NULL;
 	}
 
+	plug->error[0] = '\0';
 	retval = gaim_plugin_init(plug->handle);
 	debug_printf("loaded plugin returned %s\n", retval ? retval : "NULL");
 	if (retval) {
@@ -632,8 +633,10 @@
 
 	while (c) {
 		p = (struct gaim_plugin *)c->data;
-		if (g_module_symbol(p->handle, "gaim_plugin_remove", (gpointer *)&gaim_plugin_remove))
-			gaim_plugin_remove();
+		if (p->type == plugin) {
+			if (g_module_symbol(p->handle, "gaim_plugin_remove", (gpointer *)&gaim_plugin_remove))
+				gaim_plugin_remove();
+		}
 		g_free(p);
 		c = c->next;
 	}
--- a/src/perl.c	Sat Sep 28 03:48:28 2002 +0000
+++ b/src/perl.c	Sat Sep 28 08:08:14 2002 +0000
@@ -68,19 +68,20 @@
 	char *name;
 	char *version;
 	char *shutdowncallback; /* bleh */
+	struct gaim_plugin *plug;
 };
 
 struct _perl_event_handlers {
 	char *event_type;
 	char *handler_name;
-	char *handle;
+	struct gaim_plugin *plug;
 };
 
 struct _perl_timeout_handlers {
 	char *handler_name;
 	char *handler_args;
 	gint iotag;
-	char *handle;
+	struct gaim_plugin *plug;
 };
 
 static GList *perl_list = NULL; /* should probably extern this at some point */
@@ -203,8 +204,6 @@
 
 }
 
-/* This function is so incredibly broken and should never, ever, ever
-   be trusted to work */
 void perl_unload_file(struct gaim_plugin *plug) {
 	struct perlscript *scp = NULL;
 	struct _perl_timeout_handlers *thn;
@@ -215,29 +214,24 @@
 	debug_printf("Unloading %s\n", plug->handle);
 	while (pl) {
 		scp = pl->data;
-		/* This is so broken */
-		if (!strcmp(scp->name, plug->desc.name) &&
-		    !strcmp(scp->version, plug->desc.version))
+		if (scp->plug == plug) {
+			perl_list = g_list_remove(perl_list, scp);
+			if (scp->shutdowncallback[0])
+				execute_perl(scp->shutdowncallback, "");
+			perl_list = g_list_remove(perl_list, scp);
+			g_free(scp->name);
+			g_free(scp->version);
+			g_free(scp->shutdowncallback);
+			g_free(scp);	
 			break;
-		pl = pl->next;
-		scp = NULL;
-	}
-	if (scp) {
-		perl_list = g_list_remove(perl_list, scp);
-		if (scp->shutdowncallback[0])
-			execute_perl(scp->shutdowncallback, "");
-		perl_list = g_list_remove(perl_list, scp);
-		g_free(scp->name);
-		g_free(scp->version);
-		g_free(scp->shutdowncallback);
-		g_free(scp);
+		}
 	}
 
 	pl = perl_timeout_handlers;
 	while (pl) {
 		thn = pl->data;
-		if (thn && thn->handle == plug->handle) {
-			g_list_remove(perl_timeout_handlers, thn);
+		if (thn && thn->plug == plug) {
+			perl_timeout_handlers = g_list_remove(perl_timeout_handlers, thn);
 			g_source_remove(thn->iotag);
 			g_free(thn->handler_args);
 			g_free(thn->handler_name);
@@ -249,7 +243,7 @@
 	pl = perl_event_handlers;
 	while (pl) {
 		ehn = pl->data;
-		if (ehn && ehn->handle == plug->handle) {
+		if (ehn && ehn->plug == plug) {
 			perl_event_handlers = g_list_remove(perl_event_handlers, ehn);
 			g_free(ehn->event_type);
 			g_free(ehn->handler_name);
@@ -259,15 +253,17 @@
 	}
 
 	plug->handle=NULL;
+	plugins = g_list_remove(plugins, plug);
+	save_prefs();
 }
 
 int perl_load_file(char *script_name)
 {
 	struct gaim_plugin *plug;
 	GList *p = probed_plugins;
-	GList *e = perl_event_handlers;
-	GList *t = perl_timeout_handlers;
-	int num_e, num_t, ret;
+	GList *s;
+	struct perlscript *scp;
+	int ret;
 
 	if (my_perl == NULL)
 		perl_init();
@@ -282,36 +278,35 @@
 	if (!plug) {
 		probe_perl(script_name);
 	}
-	
+
 	plug->handle = plug->path;
-	
-	/* This is such a terrible hack-- if I weren't tired and annoyed
-	 * with perl, I'm sure I wouldn't even be considering this. */
-	num_e=g_list_length(e);
-	num_t=g_list_length(t);
+	plugins = g_list_append(plugins, plug);
 
 	ret = execute_perl("load_n_eval", script_name);
 
-	t = g_list_nth(perl_timeout_handlers, num_t++);
-	while (t) {
-		struct _perl_timeout_handlers *h = t->data;
-		h->handle = plug->handle;
-		t = t->next;
+	s = perl_list;
+	while (s) {
+		scp = s->data;
+	
+		if (!strcmp(scp->name, plug->desc.name) &&
+		    !strcmp(scp->version, plug->desc.version))
+			break;
+		s = s->next;
 	}
 
-	e = g_list_nth(perl_event_handlers, num_e++);
-	while (e) {
-		struct _perl_event_handlers *h = e->data;
-		h->handle = plug->handle;
-		e = e->next;
+	if (!s) {
+		g_snprintf(plug->error, sizeof(plug->error), _("GAIM::register not called with proper arguments.  Consult PERL-HOWTO."));
+		return 0;
 	}
+	
+	plug->error[0] = '\0';
 	return ret;
 }
 
 struct gaim_plugin *probe_perl(const char *filename) {
 
 	/* XXX This woulld be much faster if I didn't create a new
-	 *     PerlInterpreter every time I did probed a plugin */
+	 *     PerlInterpreter every time I probed a plugin */
 
 	PerlInterpreter *prober = perl_alloc();
 	struct gaim_plugin * plug = NULL;
@@ -328,7 +323,6 @@
 
 		count = perl_call_pv("description", G_NOARGS | G_ARRAY | G_EVAL);
 		SPAGAIN;
-		debug_printf("desc: %d  char: %d  count: %d\n", sizeof(struct gaim_plugin_description), sizeof(char*), count);
 		if (count == (sizeof(struct gaim_plugin_description) - sizeof(int)) / sizeof(char*)) {
 			plug = g_new0(struct gaim_plugin, 1);
 			plug->type = perl_script;
@@ -449,6 +443,9 @@
 	char *name, *ver, *callback, *unused; /* exactly like X-Chat, eh? :) */
 	unsigned int junk;
 	struct perlscript *scp;
+	struct gaim_plugin *plug;
+	GList *pl = plugins;
+	
 	dXSARGS;
 	items = 0;
 
@@ -457,13 +454,25 @@
 	callback = SvPV (ST (2), junk);
 	unused = SvPV (ST (3), junk);
 
-	scp = g_new0(struct perlscript, 1);
-	scp->name = g_strdup(name);
-	scp->version = g_strdup(ver);
-	scp->shutdowncallback = g_strdup(callback);
-	perl_list = g_list_append(perl_list, scp);
+	while (pl) {
+		plug = pl->data;
+
+		if (!strcmp(name, plug->desc.name) &&
+		    !strcmp(ver, plug->desc.version)) {
+			break;
+		}
+		pl = pl->next;
+	}
 
-	XST_mPV (0, VERSION);
+	if (plug) {
+		scp = g_new0(struct perlscript, 1);
+		scp->name = g_strdup(name);
+		scp->version = g_strdup(ver);
+		scp->shutdowncallback = g_strdup(callback);
+		scp->plug = plug; 
+		perl_list = g_list_append(perl_list, scp);
+	}
+	XST_mPV (0, plug->path);
 	XSRETURN (1);
 }
 
@@ -963,14 +972,31 @@
 {
 	unsigned int junk;
 	struct _perl_event_handlers *handler;
+	char *handle;
+	struct gaim_plugin *plug;
+	GList *p = plugins;
 	dXSARGS;
 	items = 0;
+	
+	handle = SvPV(ST(0), junk);
+	while (p) {
+		plug = p->data;
+		if (!strcmp(handle, plug->path))
+			break;
+		p = p->next;
+	}
 
-	handler = g_new0(struct _perl_event_handlers, 1);
-	handler->event_type = g_strdup(SvPV(ST(0), junk));
-	handler->handler_name = g_strdup(SvPV(ST(1), junk));
-	perl_event_handlers = g_list_append(perl_event_handlers, handler);
-	debug_printf("registered perl event handler for %s\n", handler->event_type);
+	if (p) {
+		handler = g_new0(struct _perl_event_handlers, 1);
+		handler->event_type = g_strdup(SvPV(ST(1), junk));
+		handler->handler_name = g_strdup(SvPV(ST(2), junk));
+		handler->plug = plug;
+		perl_event_handlers = g_list_append(perl_event_handlers, handler);
+		debug_printf("registered perl event handler for %s\n", handler->event_type);
+	} else {
+		debug_printf("Invalid handle (%s) registering perl event handler\n", handle);
+	}
+	
 	XSRETURN_EMPTY;
 }
 
@@ -1015,16 +1041,33 @@
 	unsigned int junk;
 	long timeout;
 	struct _perl_timeout_handlers *handler;
+	char *handle;
+	struct gaim_plugin *plug;
+	GList *p = plugins;
+	
 	dXSARGS;
 	items = 0;
+	
+	handle = SvPV(ST(0), junk);
+	while (p) {
+		plug = p->data;
+		if (!strcmp(handle, plug->path))
+			break;
+		p = p->next;
+	}
 
-	handler = g_new0(struct _perl_timeout_handlers, 1);
-	timeout = 1000 * SvIV(ST(0));
-	debug_printf("Adding timeout for %d seconds.\n", timeout/1000);
-	handler->handler_name = g_strdup(SvPV(ST(1), junk));
-	handler->handler_args = g_strdup(SvPV(ST(2), junk));
-	perl_timeout_handlers = g_list_append(perl_timeout_handlers, handler);
-	handler->iotag = g_timeout_add(timeout, perl_timeout, handler);
+	if (p) {
+		handler = g_new0(struct _perl_timeout_handlers, 1);
+		timeout = 1000 * SvIV(ST(1));
+		debug_printf("Adding timeout for %d seconds.\n", timeout/1000);
+		handler->plug = plug;
+		handler->handler_name = g_strdup(SvPV(ST(2), junk));
+		handler->handler_args = g_strdup(SvPV(ST(3), junk));
+		perl_timeout_handlers = g_list_append(perl_timeout_handlers, handler);
+		handler->iotag = g_timeout_add(timeout, perl_timeout, handler);
+	} else {
+		debug_printf("Invalid handle (%s) in adding perl timeout handler.", handle);
+	}
 	XSRETURN_EMPTY;
 }
 
@@ -1046,21 +1089,5 @@
 	perl_init();
 }
 
-extern void list_perl_scripts()
-{
-	GList *s = perl_list;
-	struct perlscript *p;
-	char buf[BUF_LONG * 4];
-	int at = 0;
-
-	at += g_snprintf(buf + at, sizeof(buf) - at, "Loaded scripts:\n");
-	while (s) {
-		p = (struct perlscript *)s->data;
-		at += g_snprintf(buf + at, sizeof(buf) - at, "%s\n", p->name);
-		s = s->next;
-	}
-
-	do_error_dialog(buf, NULL, GAIM_INFO);
-}
 
 #endif /* USE_PERL */
--- a/src/prefs.c	Sat Sep 28 03:48:28 2002 +0000
+++ b/src/prefs.c	Sat Sep 28 08:08:14 2002 +0000
@@ -861,8 +861,13 @@
 	gtk_tree_model_get_value (model, &iter, 2, &val);
 	plug = g_value_get_pointer(&val);
 	
-	g_snprintf(buf, sizeof(buf), _("<span size=\"larger\">%s %s</span>\n\n%s"), 
-		   plug->desc.name, plug->desc.version, plug->desc.description);
+	if (plug->error[0]) 
+		g_snprintf(buf, sizeof(buf), _("<span size=\"larger\">%s %s</span>\n\n"
+					       "<span weight=\"bold\" color=\"red\">%s</span>\n\n"
+					       "%s"), plug->desc.name, plug->desc.version, plug->error, plug->desc.description); 
+	else
+		g_snprintf(buf, sizeof(buf), _("<span size=\"larger\">%s %s</span>\n\n"
+					       "%s"), plug->desc.name, plug->desc.version, plug->desc.description); 
 	gtk_label_set_markup(GTK_LABEL(plugin_description), buf);
 	g_snprintf(buf, sizeof(buf), _("<span size=\"larger\">%s %s</span>\n\n"
 				       "<span weight=\"bold\">Written by:</span>\t%s\n"
@@ -915,10 +920,16 @@
 	                {}
 #endif
 	gdk_window_set_cursor(prefs->window, NULL);
-	
+	if (plug->error[0]) 
+		g_snprintf(buf, sizeof(buf), _("<span size=\"larger\">%s %s</span>\n\n"
+					       "<span weight=\"bold\" color=\"red\">%s</span>\n\n"
+					       "%s"), plug->desc.name, plug->desc.version, plug->error, plug->desc.description); 
+	else
+		g_snprintf(buf, sizeof(buf), _("<span size=\"larger\">%s %s</span>\n\n"
+					       "%s"), plug->desc.name, plug->desc.version, plug->desc.description); 
+	gtk_label_set_markup(GTK_LABEL(plugin_description), buf);
 	gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, plug->handle, -1);
-	g_snprintf(buf, sizeof(buf), _("<span size=\"larger\">%s %s</span>\n\n%s"), 
-		   plug->desc.name, plug->desc.version, plug->desc.description);
+	
 	gtk_label_set_markup(GTK_LABEL(plugin_description), buf);
 	gtk_tree_path_free(path);
 }