summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/expandos.c2
-rw-r--r--src/core/expandos.h2
-rw-r--r--src/core/special-vars.c14
-rw-r--r--src/perl/Makefile.am1
-rw-r--r--src/perl/common/Expando.xs170
-rw-r--r--src/perl/common/Irssi.xs3
-rw-r--r--src/perl/common/Server.xs2
-rw-r--r--src/perl/textui/Statusbar.xs1
8 files changed, 192 insertions, 3 deletions
diff --git a/src/core/expandos.c b/src/core/expandos.c
index 8f97836a..7ebc4f20 100644
--- a/src/core/expandos.c
+++ b/src/core/expandos.c
@@ -46,6 +46,8 @@ typedef struct {
int signal_args[MAX_EXPANDO_SIGNALS];
} EXPANDO_REC;
+const char *current_expando = NULL;
+
static int timer_tag;
static EXPANDO_REC *char_expandos[255];
diff --git a/src/core/expandos.h b/src/core/expandos.h
index cf057994..45608a26 100644
--- a/src/core/expandos.h
+++ b/src/core/expandos.h
@@ -16,6 +16,8 @@ typedef enum {
typedef char* (*EXPANDO_FUNC)
(SERVER_REC *server, void *item, int *free_ret);
+extern const char *current_expando;
+
/* Create expando - overrides any existing ones.
... = signal, type, ..., NULL - list of signals that might change the
value of this expando */
diff --git a/src/core/special-vars.c b/src/core/special-vars.c
index 7291f469..f7d69728 100644
--- a/src/core/special-vars.c
+++ b/src/core/special-vars.c
@@ -113,8 +113,10 @@ static char *get_long_variable_value(const char *key, SERVER_REC *server,
/* expando? */
func = expando_find_long(key);
- if (func != NULL)
+ if (func != NULL) {
+ current_expando = key;
return func(server, item, free_ret);
+ }
/* internal setting? */
type = settings_get_type(key);
@@ -176,7 +178,15 @@ static char *get_variable(char **cmd, SERVER_REC *server, void *item,
}
*free_ret = FALSE;
func = expando_find_char(**cmd);
- return func == NULL ? NULL : func(server, item, free_ret);
+ if (func == NULL)
+ return NULL;
+ else {
+ char str[2];
+
+ str[0] = **cmd; str[1] = '\0';
+ current_expando = str;
+ return func(server, item, free_ret);
+ }
}
static char *get_history(char **cmd, void *item, int *free_ret)
diff --git a/src/perl/Makefile.am b/src/perl/Makefile.am
index 1c837e28..293bd80c 100644
--- a/src/perl/Makefile.am
+++ b/src/perl/Makefile.am
@@ -78,6 +78,7 @@ common_sources = \
common/Irssi.pm \
common/Channel.xs \
common/Core.xs \
+ common/Expandos.xs \
common/Ignore.xs \
common/Log.xs \
common/Masks.xs \
diff --git a/src/perl/common/Expando.xs b/src/perl/common/Expando.xs
new file mode 100644
index 00000000..d3a232f7
--- /dev/null
+++ b/src/perl/common/Expando.xs
@@ -0,0 +1,170 @@
+#include "module.h"
+#include "expandos.h"
+
+typedef struct {
+ PERL_SCRIPT_REC *script;
+ SV *func;
+} PerlExpando;
+
+static GHashTable *perl_expando_defs;
+
+static char *sig_perl_expando(SERVER_REC *server, void *item, int *free_ret);
+
+static int check_expando_destroy(char *key, PerlExpando *rec,
+ PERL_SCRIPT_REC *script)
+{
+ if (rec->script == script) {
+ expando_destroy(key, sig_perl_expando);
+ SvREFCNT_dec(rec->func);
+ g_free(key);
+ g_free(rec);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void script_unregister_expandos(PERL_SCRIPT_REC *script)
+{
+ g_hash_table_foreach_remove(perl_expando_defs,
+ (GHRFunc) check_expando_destroy, script);
+}
+
+void perl_expando_init(void)
+{
+ perl_expando_defs = g_hash_table_new((GHashFunc) g_str_hash,
+ (GCompareFunc) g_str_equal);
+ signal_add("script destroyed", (SIGNAL_FUNC) script_unregister_expandos);
+}
+
+static void expando_def_destroy(char *key, PerlExpando *rec)
+{
+ SvREFCNT_dec(rec->func);
+ g_free(key);
+ g_free(rec);
+}
+
+void perl_expando_deinit(void)
+{
+ signal_remove("script destroyed", (SIGNAL_FUNC) script_unregister_expandos);
+
+ g_hash_table_foreach(perl_expando_defs,
+ (GHFunc) expando_def_destroy, NULL);
+ g_hash_table_destroy(perl_expando_defs);
+}
+
+static char *perl_expando_event(PerlExpando *rec, SERVER_REC *server,
+ WI_ITEM_REC *item, int *free_ret)
+{
+ dSP;
+ char *ret;
+ int retcount;
+
+ ENTER;
+ SAVETMPS;
+
+ PUSHMARK(SP);
+ XPUSHs(sv_2mortal(iobject_bless(server)));
+ XPUSHs(sv_2mortal(iobject_bless(item)));
+ PUTBACK;
+
+ retcount = perl_call_sv(rec->func, G_EVAL|G_SCALAR);
+ SPAGAIN;
+
+ if (SvTRUE(ERRSV)) {
+ /* make sure we don't get back here */
+ if (rec->script != NULL)
+ script_unregister_expandos(rec->script);
+
+ signal_emit("script error", 2, rec->script, SvPV(ERRSV, PL_na));
+ ret = NULL;
+ } else if (retcount > 0) {
+ ret = g_strdup(POPp);
+ *free_ret = TRUE;
+ }
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return ret;
+}
+
+static char *sig_perl_expando(SERVER_REC *server, void *item, int *free_ret)
+{
+ PerlExpando *rec;
+
+ rec = g_hash_table_lookup(perl_expando_defs, current_expando);
+ if (rec != NULL)
+ return perl_expando_event(rec, server, item, free_ret);
+ return NULL;
+}
+
+void expando_signals_add_hash(const char *key, SV *signals)
+{
+ HV *hv;
+ HE *he;
+ I32 len;
+ const char *argstr;
+ ExpandoArg arg;
+
+ if (!is_hvref(signals)) {
+ croak("Usage: Irssi::expando_create(key, func, hash)");
+ return;
+ }
+
+ hv = hvref(signals);
+ hv_iterinit(hv);
+ while ((he = hv_iternext(hv)) != NULL) {
+ SV *argsv = HeVAL(he);
+ argstr = SvPV(argsv, PL_na);
+
+ if (strcasecmp(argstr, "none") == 0)
+ arg = EXPANDO_ARG_NONE;
+ else if (strcasecmp(argstr, "server") == 0)
+ arg = EXPANDO_ARG_SERVER;
+ else if (strcasecmp(argstr, "window") == 0)
+ arg = EXPANDO_ARG_WINDOW;
+ else if (strcasecmp(argstr, "windowitem") == 0)
+ arg = EXPANDO_ARG_WINDOW_ITEM;
+ else if (strcasecmp(argstr, "never") == 0)
+ arg = EXPANDO_NEVER;
+ else {
+ croak("Unknown signal type: %s", argstr);
+ break;
+ }
+ expando_add_signal(key, hv_iterkey(he, &len), arg);
+ }
+}
+
+MODULE = Irssi::Expando PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+void
+expando_create(key, func, signals)
+ char *key
+ SV *func
+ SV *signals
+PREINIT:
+ PerlExpando *rec;
+CODE:
+ rec = g_new0(PerlExpando, 1);
+ rec->script = perl_script_find_package(perl_get_package());
+ rec->func = perl_func_sv_inc(func, perl_get_package());
+
+ expando_create(key, sig_perl_expando, NULL);
+ g_hash_table_insert(perl_expando_defs, g_strdup(key), rec);
+ expando_signals_add_hash(key, signals);
+
+void
+expando_destroy(name)
+ char *name
+PREINIT:
+ gpointer key, value;
+CODE:
+ if (g_hash_table_lookup_extended(perl_expando_defs, name, &key, &value)) {
+ g_hash_table_remove(perl_expando_defs, name);
+ g_free(key);
+ SvREFCNT_dec((SV *) value);
+ }
+ expando_destroy(name, sig_perl_expando);
diff --git a/src/perl/common/Irssi.xs b/src/perl/common/Irssi.xs
index 67d5e96c..328e32d3 100644
--- a/src/perl/common/Irssi.xs
+++ b/src/perl/common/Irssi.xs
@@ -14,16 +14,19 @@ CODE:
initialized = TRUE;
perl_settings_init();
+ perl_expando_init();
void
deinit()
CODE:
if (!initialized) return;
+ perl_expando_deinit();
perl_settings_deinit();
BOOT:
irssi_boot(Channel);
irssi_boot(Core);
+ irssi_boot(Expando);
irssi_boot(Ignore);
irssi_boot(Log);
irssi_boot(Masks);
diff --git a/src/perl/common/Server.xs b/src/perl/common/Server.xs
index adcabb16..facf1da0 100644
--- a/src/perl/common/Server.xs
+++ b/src/perl/common/Server.xs
@@ -31,7 +31,7 @@ PPCODE:
}
Irssi::Connect
-server_create_conn(chat_type, dest, port=6667, chatnet=NULL, password=NULL, nick=NULL)
+server_create_conn(chat_type, dest, port, chatnet=NULL, password=NULL, nick=NULL)
int chat_type
char *dest
int port
diff --git a/src/perl/textui/Statusbar.xs b/src/perl/textui/Statusbar.xs
index 67b88e40..522a93ee 100644
--- a/src/perl/textui/Statusbar.xs
+++ b/src/perl/textui/Statusbar.xs
@@ -31,6 +31,7 @@ void perl_statusbar_init(void)
static void statusbar_item_def_destroy(void *key, void *value)
{
+ statusbar_item_unregister(key);
g_free(key);
g_free(value);
}