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); }