summaryrefslogtreecommitdiff
path: root/src/perl
diff options
context:
space:
mode:
Diffstat (limited to 'src/perl')
-rw-r--r--src/perl/Makefile.am30
-rwxr-xr-xsrc/perl/get-signals.pl38
-rw-r--r--src/perl/irssi-perl.c529
-rw-r--r--src/perl/irssi-perl.h7
-rw-r--r--src/perl/module.h3
-rw-r--r--src/perl/perl-signals.h108
-rw-r--r--src/perl/xs/Irssi.pm21
-rw-r--r--src/perl/xs/Irssi.xs933
-rw-r--r--src/perl/xs/Makefile.PL.in6
-rw-r--r--src/perl/xs/typemap14
-rw-r--r--src/perl/xsinit.c41
11 files changed, 1730 insertions, 0 deletions
diff --git a/src/perl/Makefile.am b/src/perl/Makefile.am
new file mode 100644
index 00000000..4093d2ad
--- /dev/null
+++ b/src/perl/Makefile.am
@@ -0,0 +1,30 @@
+noinst_LTLIBRARIES = libperl.la
+
+libperl_la_DEPENDENCIES = perl-signals.h
+
+INCLUDES = $(GLIB_CFLAGS) \
+ -DPLUGINSDIR=\""$(libdir)/irssi/plugins"\" \
+ -DSCRIPTDIR=\""$(libdir)/irssi/scripts"\" \
+ $(PERL_CFLAGS) \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/src/core
+
+libperl_la_SOURCES = \
+ irssi-perl.c \
+ xsinit.c
+
+perl-signals.h: $(top_srcdir)/docs/signals.txt $(srcdir)/get-signals.pl
+ cat $(top_srcdir)/docs/signals.txt | $(perlpath) $(srcdir)/get-signals.pl > perl-signals.h
+
+EXTRA_DIST = \
+ get-signals.pl \
+ xs/Irssi.xs \
+ xs/Irssi.pm \
+ xs/Makefile.PL.in \
+ xs/typemap
+
+all-local:
+ cd xs && if [ ! -f Makefile ]; then $(perlpath) Makefile.PL; fi && $(MAKE) && cd ..
+
+install-exec-local:
+ cd xs && make install && cd ..
diff --git a/src/perl/get-signals.pl b/src/perl/get-signals.pl
new file mode 100755
index 00000000..73b6401f
--- /dev/null
+++ b/src/perl/get-signals.pl
@@ -0,0 +1,38 @@
+#!/usr/bin/perl
+
+print "static PERL_SIGNAL_ARGS_REC perl_signal_args[] =\n{\n";
+
+while (<STDIN>) {
+ chomp;
+
+ last if (/UI common/);
+ next if (!/^ "([^"]*)"(<.*>)?,\s*(.*)/);
+ next if (/\.\.\./);
+ next if (/\(/);
+
+ $signal = $1;
+ $_ = $3;
+
+ s/char \*[^,]*/string/g;
+ s/ulong \*[^,]*/ulongptr/g;
+ s/int[^,]*/int/g;
+ s/GSList of (\w+)s/gslist_\1/g;
+
+ s/SERVER_REC \*[^,]*/Irssi::Server/g;
+ s/RECONNECT_REC \*[^,]*/Irssi::Reconnect/g;
+ s/CHANNEL_REC \*[^,]*/Irssi::Channel/g;
+ s/COMMAND_REC \*[^,]*/Irssi::Command/g;
+ s/NICK_REC \*[^,]*/Irssi::Nick/g;
+ s/BAN_REC \*[^,]*/Irssi::Ban/g;
+ s/NETSPLIT_REC \*[^,]*/Irssi::Netsplit/g;
+ s/DCC_REC \*[^,]*/Irssi::Dcc/g;
+ s/LOG_REC \*[^,]*/Irssi::Log/g;
+ s/LOG_ITEM_REC \*[^,]*/Irssi::Logitem/g;
+ s/PLUGIN_REC \*[^,]*/Irssi::Plugin/g;
+ s/AUTOIGNORE_REC \*[^,]*/Irssi::Autoignore/g;
+
+ s/([\w:]+)(,|$)/"\1"\2/g;
+ print " { -1, \"$signal\", { $_, NULL } },\n";
+}
+
+print "\n { -1, NULL }\n};\n";
diff --git a/src/perl/irssi-perl.c b/src/perl/irssi-perl.c
new file mode 100644
index 00000000..494e0da5
--- /dev/null
+++ b/src/perl/irssi-perl.c
@@ -0,0 +1,529 @@
+/*
+ perl.c : irssi
+
+ Copyright (C) 1999 Timo Sirainen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <EXTERN.h>
+#ifndef _SEM_SEMUN_UNDEFINED
+#define HAS_UNION_SEMUN
+#endif
+#include <perl.h>
+
+#undef _
+#undef PACKAGE
+
+#include "module.h"
+#include "modules.h"
+#include "signals.h"
+#include "commands.h"
+
+extern void xs_init(void);
+
+typedef struct {
+ int signal_id;
+ char *signal;
+ char *args[7];
+} PERL_SIGNAL_ARGS_REC;
+
+typedef struct {
+ char *signal;
+ int signal_id;
+
+ char *func;
+ int last;
+} PERL_SIGNAL_REC;
+
+typedef struct {
+ int tag;
+ char *func;
+ char *data;
+} PERL_TIMEOUT_REC;
+
+#include "perl-signals.h"
+
+static GHashTable *first_signals, *last_signals;
+static GSList *perl_timeouts;
+static PerlInterpreter *irssi_perl_interp;
+static int signal_grabbed, siglast_grabbed;
+
+static void sig_signal(void *signal, ...);
+static void sig_lastsignal(void *signal, ...);
+
+static void perl_signal_destroy(PERL_SIGNAL_REC *rec)
+{
+ GHashTable *table;
+ GSList *siglist;
+ void *signal_idp;
+
+ table = rec->last ? last_signals : first_signals;
+ signal_idp = GINT_TO_POINTER(rec->signal_id);
+
+ siglist = g_hash_table_lookup(table, signal_idp);
+ if (siglist == NULL) return;
+
+ siglist = g_slist_remove(siglist, rec);
+ g_hash_table_remove(table, signal_idp);
+ if (siglist != NULL) g_hash_table_insert(table, signal_idp, siglist);
+
+ if (!rec->last && signal_grabbed && g_hash_table_size(first_signals) == 0) {
+ signal_grabbed = FALSE;
+ signal_remove("signal", (SIGNAL_FUNC) sig_signal);
+ }
+
+ if (rec->last && siglast_grabbed && g_hash_table_size(last_signals) == 0) {
+ siglast_grabbed = FALSE;
+ signal_remove("last signal", (SIGNAL_FUNC) sig_lastsignal);
+ }
+
+ if (strncmp(rec->signal, "command ", 8) == 0)
+ command_unbind(rec->signal+8, NULL);
+
+ g_free(rec->signal);
+ g_free(rec->func);
+ g_free(rec);
+}
+
+static void perl_timeout_destroy(PERL_TIMEOUT_REC *rec)
+{
+ perl_timeouts = g_slist_remove(perl_timeouts, rec);
+
+ g_source_remove(rec->tag);
+ g_free(rec->func);
+ g_free(rec->data);
+ g_free(rec);
+}
+
+static void irssi_perl_start(void)
+{
+ /* stolen from xchat, thanks :) */
+ char *args[] = {"", "-e", "0"};
+ char load_file[] =
+ "sub load_file()\n"
+ "{\n"
+ " (my $file_name) = @_;\n"
+ " open FH, $file_name or return 2;\n"
+ " local($/) = undef;\n"
+ " $file = <FH>;\n"
+ " close FH;\n"
+ " eval $file;\n"
+ " eval $file if $@;\n"
+ " return 1 if $@;\n"
+ " return 0;\n"
+ "}";
+
+ first_signals = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal);
+ last_signals = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal);
+ perl_timeouts = NULL;
+
+ irssi_perl_interp = perl_alloc();
+ perl_construct(irssi_perl_interp);
+
+ perl_parse(irssi_perl_interp, xs_init, 3, args, NULL);
+ perl_eval_pv(load_file, TRUE);
+}
+
+static void signal_destroy_hash(void *key, PERL_SIGNAL_REC *rec)
+{
+ perl_signal_destroy(rec);
+}
+
+static void irssi_perl_stop(void)
+{
+ g_hash_table_foreach(first_signals, (GHFunc) signal_destroy_hash, NULL);
+ g_hash_table_destroy(first_signals);
+ g_hash_table_foreach(last_signals, (GHFunc) signal_destroy_hash, NULL);
+ g_hash_table_destroy(last_signals);
+
+ while (perl_timeouts != NULL)
+ perl_timeout_destroy(perl_timeouts->data);
+
+ perl_destruct(irssi_perl_interp);
+ perl_free(irssi_perl_interp);
+ irssi_perl_interp = NULL;
+}
+
+static void cmd_run(char *data)
+{
+ dSP;
+ struct stat statbuf;
+ char *fname;
+ int retcount;
+
+ /* add .pl suffix if it's missing */
+ data = (strlen(data) <= 3 || strcmp(data+strlen(data)-3, ".pl") == 0) ?
+ g_strdup(data) : g_strdup_printf("%s.pl", data);
+
+ if (g_path_is_absolute(data)) {
+ /* whole path specified */
+ fname = g_strdup(data);
+ } else {
+ /* check from ~/.irssi/scripts/ */
+ fname = g_strdup_printf("%s/.irssi/scripts/%s", g_get_home_dir(), data);
+ if (stat(fname, &statbuf) != 0) {
+ /* check from SCRIPTDIR */
+ g_free(fname),
+ fname = g_strdup_printf(SCRIPTDIR"/%s", data);
+ }
+ }
+ g_free(data);
+
+ ENTER;
+ SAVETMPS;
+
+ PUSHMARK(SP);
+ XPUSHs(sv_2mortal(newSVpv(fname, strlen(fname)))); g_free(fname);
+ PUTBACK;
+
+ retcount = perl_call_pv("load_file", G_EVAL|G_SCALAR);
+ SPAGAIN;
+
+ if (SvTRUE(ERRSV)) {
+ STRLEN n_a;
+
+ signal_emit("perl error", 1, SvPV(ERRSV, n_a));
+ (void) POPs;
+ }
+ else while (retcount--) (void) POPi;
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+}
+
+static void cmd_flush(const char *data)
+{
+ irssi_perl_stop();
+ irssi_perl_start();
+}
+
+static void perl_signal_to(const char *signal, const char *func, int last)
+{
+ PERL_SIGNAL_REC *rec;
+ GHashTable *table;
+ GSList *siglist;
+ void *signal_idp;
+
+ rec = g_new(PERL_SIGNAL_REC, 1);
+ rec->signal_id = module_get_uniq_id_str("signals", signal);
+ rec->signal = g_strdup(signal);
+ rec->func = g_strdup(func);
+ rec->last = last;
+
+ table = last ? last_signals : first_signals;
+ signal_idp = GINT_TO_POINTER(rec->signal_id);
+
+ siglist = g_hash_table_lookup(table, signal_idp);
+ if (siglist != NULL) g_hash_table_remove(table, signal_idp);
+
+ siglist = g_slist_append(siglist, rec);
+ g_hash_table_insert(table, signal_idp, siglist);
+
+ if (!last && !signal_grabbed) {
+ signal_grabbed = TRUE;
+ signal_add("signal", (SIGNAL_FUNC) sig_signal);
+ } else if (last && !siglast_grabbed) {
+ siglast_grabbed = TRUE;
+ signal_add("last signal", (SIGNAL_FUNC) sig_lastsignal);
+ }
+}
+
+void perl_signal_add(const char *signal, const char *func)
+{
+ perl_signal_to(signal, func, FALSE);
+}
+
+void perl_signal_add_last(const char *signal, const char *func)
+{
+ perl_signal_to(signal, func, TRUE);
+}
+
+static void perl_signal_remove_list(GSList *list, const char *func)
+{
+ while (list != NULL) {
+ PERL_SIGNAL_REC *rec = list->data;
+
+ if (strcmp(func, rec->func) == 0) {
+ perl_signal_destroy(rec);
+ break;
+ }
+
+ list = list->next;
+ }
+}
+
+void perl_signal_remove(const char *signal, const char *func)
+{
+ GSList *list;
+ int signal_id;
+
+ signal_id = module_get_uniq_id_str("signals", signal);
+
+ list = g_hash_table_lookup(first_signals, GINT_TO_POINTER(signal_id));
+ if (list != NULL)
+ perl_signal_remove_list(list, func);
+ else {
+ list = g_hash_table_lookup(last_signals, GINT_TO_POINTER(signal_id));
+ if (list != NULL) perl_signal_remove_list(list, func);
+ }
+}
+
+static int perl_timeout(PERL_TIMEOUT_REC *rec)
+{
+ dSP;
+ int retcount;
+
+ ENTER;
+ SAVETMPS;
+
+ PUSHMARK(SP);
+ XPUSHs(sv_2mortal(newSVpv(rec->data, strlen(rec->data))));
+ PUTBACK;
+
+ retcount = perl_call_pv(rec->func, G_EVAL|G_SCALAR);
+ SPAGAIN;
+
+ if (SvTRUE(ERRSV)) {
+ STRLEN n_a;
+
+ signal_emit("perl error", 1, SvPV(ERRSV, n_a));
+ (void) POPs;
+ }
+ else while (retcount--) (void) POPi;
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return 1;
+}
+
+int perl_timeout_add(int msecs, const char *func, const char *data)
+{
+ PERL_TIMEOUT_REC *rec;
+
+ rec = g_new(PERL_TIMEOUT_REC, 1);
+ rec->func = g_strdup(func);
+ rec->data = g_strdup(data);
+ rec->tag = g_timeout_add(msecs, (GSourceFunc) perl_timeout, rec);
+
+ perl_timeouts = g_slist_append(perl_timeouts, rec);
+ return rec->tag;
+}
+
+void perl_timeout_remove(int tag)
+{
+ GSList *tmp;
+
+ for (tmp = perl_timeouts; tmp != NULL; tmp = tmp->next) {
+ PERL_TIMEOUT_REC *rec = tmp->data;
+
+ if (rec->tag == tag) {
+ perl_timeout_destroy(rec);
+ break;
+ }
+ }
+}
+
+static int call_perl(const char *func, int signal, va_list va)
+{
+ dSP;
+ PERL_SIGNAL_ARGS_REC *rec;
+ int retcount, n, ret;
+ void *arg;
+ HV *stash;
+
+ /* first check if we find exact match */
+ rec = NULL;
+ for (n = 0; perl_signal_args[n].signal != NULL; n++)
+ {
+ if (signal == perl_signal_args[n].signal_id)
+ {
+ rec = &perl_signal_args[n];
+ break;
+ }
+ }
+
+ if (rec == NULL)
+ {
+ /* try to find by name */
+ const char *signame;
+
+ signame = module_find_id_str("signals", signal);
+ for (n = 0; perl_signal_args[n].signal != NULL; n++)
+ {
+ if (strncmp(signame, perl_signal_args[n].signal,
+ strlen(perl_signal_args[n].signal)) == 0)
+ {
+ rec = &perl_signal_args[n];
+ break;
+ }
+ }
+ }
+
+ ENTER;
+ SAVETMPS;
+
+ PUSHMARK(sp);
+
+ if (rec != NULL)
+ {
+ /* put the arguments to perl stack */
+ for (n = 0; n < 7; n++)
+ {
+ arg = va_arg(va, gpointer);
+
+ if (rec->args[n] == NULL)
+ break;
+
+ if (strcmp(rec->args[n], "string") == 0)
+ XPUSHs(sv_2mortal(newSVpv(arg == NULL ? "" : arg, arg == NULL ? 0 : strlen(arg))));
+ else if (strcmp(rec->args[n], "int") == 0)
+ XPUSHs(sv_2mortal(newSViv(GPOINTER_TO_INT(arg))));
+ else if (strcmp(rec->args[n], "ulongptr") == 0)
+ XPUSHs(sv_2mortal(newSViv(*(gulong *) arg)));
+ else if (strncmp(rec->args[n], "glist_", 6) == 0)
+ {
+ GSList *tmp;
+
+ stash = gv_stashpv(rec->args[n]+6, 0);
+ for (tmp = arg; tmp != NULL; tmp = tmp->next)
+ XPUSHs(sv_2mortal(sv_bless(newRV_noinc(newSViv(GPOINTER_TO_INT(tmp->data))), stash)));
+ }
+ else
+ {
+ stash = gv_stashpv(rec->args[n], 0);
+ XPUSHs(sv_2mortal(sv_bless(newRV_noinc(newSViv(GPOINTER_TO_INT(arg))), stash)));
+ }
+ }
+ }
+
+ PUTBACK;
+ retcount = perl_call_pv((char *) func, G_EVAL|G_SCALAR);
+ SPAGAIN;
+
+ ret = 0;
+ if (SvTRUE(ERRSV))
+ {
+ STRLEN n_a;
+
+ signal_emit("perl error", 1, SvPV(ERRSV, n_a));
+ (void)POPs;
+ }
+ else
+ {
+ SV *sv;
+
+ if (retcount > 0)
+ {
+ sv = POPs;
+ if (SvIOK(sv) && SvIV(sv) == 1) ret = 1;
+ }
+ for (n = 2; n <= retcount; n++)
+ (void)POPi;
+ }
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return ret;
+}
+
+static void sig_signal(void *signal, ...)
+{
+ GSList *list;
+ va_list va;
+
+ va_start(va, signal);
+
+ list = g_hash_table_lookup(first_signals, signal);
+ while (list != NULL) {
+ PERL_SIGNAL_REC *rec = list->data;
+
+ if (call_perl(rec->func, GPOINTER_TO_INT(signal), va)) {
+ signal_stop();
+ return;
+ }
+ list = list->next;
+ }
+
+ va_end(va);
+}
+
+static void sig_lastsignal(void *signal, ...)
+{
+ GSList *list;
+ va_list va;
+
+ va_start(va, signal);
+
+ list = g_hash_table_lookup(last_signals, signal);
+ while (list != NULL) {
+ PERL_SIGNAL_REC *rec = list->data;
+
+ if (call_perl(rec->func, GPOINTER_TO_INT(signal), va)) {
+ signal_stop();
+ return;
+ }
+ list = list->next;
+ }
+
+ va_end(va);
+}
+
+static void irssi_perl_autorun(void)
+{
+ DIR *dirp;
+ struct dirent *dp;
+ char *path, *fname;
+
+ path = g_strdup_printf("%s/.irssi/scripts/autorun", g_get_home_dir());
+ dirp = opendir(path);
+ if (dirp == NULL) {
+ g_free(path);
+ return;
+ }
+
+ while ((dp = readdir(dirp)) != NULL) {
+ fname = g_strdup_printf("%s/%s", path, dp->d_name);
+ cmd_run(fname);
+ g_free(fname);
+ }
+ closedir(dirp);
+ g_free(path);
+}
+
+void irssi_perl_init(void)
+{
+ command_bind("run", NULL, (SIGNAL_FUNC) cmd_run);
+ command_bind("perlflush", NULL, (SIGNAL_FUNC) cmd_flush);
+ signal_grabbed = siglast_grabbed = FALSE;
+
+ irssi_perl_start();
+ irssi_perl_autorun();
+}
+
+void irssi_perl_deinit(void)
+{
+ irssi_perl_stop();
+
+ command_unbind("run", (SIGNAL_FUNC) cmd_run);
+ command_unbind("perlflush", (SIGNAL_FUNC) cmd_flush);
+ if (signal_grabbed) signal_remove("signal", (SIGNAL_FUNC) sig_signal);
+ if (siglast_grabbed) signal_remove("last signal", (SIGNAL_FUNC) sig_lastsignal);
+}
diff --git a/src/perl/irssi-perl.h b/src/perl/irssi-perl.h
new file mode 100644
index 00000000..b26ac3b0
--- /dev/null
+++ b/src/perl/irssi-perl.h
@@ -0,0 +1,7 @@
+#ifndef __IRSSI_PERL_H
+#define __IRSSI_PERL_H
+
+void irssi_perl_init(void);
+void irssi_perl_deinit(void);
+
+#endif
diff --git a/src/perl/module.h b/src/perl/module.h
new file mode 100644
index 00000000..986e32e1
--- /dev/null
+++ b/src/perl/module.h
@@ -0,0 +1,3 @@
+#include "common.h"
+
+#define MODULE_NAME "irssi-perl"
diff --git a/src/perl/perl-signals.h b/src/perl/perl-signals.h
new file mode 100644
index 00000000..654c1a51
--- /dev/null
+++ b/src/perl/perl-signals.h
@@ -0,0 +1,108 @@
+static PERL_SIGNAL_ARGS_REC perl_signal_args[] =
+{
+ { -1, "gui channel open", { "CHANNEL_REC", NULL } },
+ { -1, "send command", { "string", "SERVER_REC", "CHANNEL_REC", NULL } },
+ { -1, "send command", { "string", "SERVER_REC", NULL } },
+ { -1, "command ", { "string", "SERVER_REC", "CHANNEL_REC", NULL } },
+ { -1, "default command", { "string", "SERVER_REC", "CHANNEL_REC", NULL } },
+ { -1, "server event", { "string", "SERVER_REC", "string", "string", NULL } },
+ { -1, "event ", { "string", "SERVER_REC", "string", "string", NULL } },
+ { -1, "default event", { "string", "SERVER_REC", "string", "string", NULL } },
+ { -1, "server incoming", { "SERVER_REC", "string", NULL } },
+ { -1, "redir ", { "string", "SERVER_REC", "string", "string", NULL } },
+ { -1, "ban new", { "BAN_REC", NULL } },
+ { -1, "ban remove", { "BAN_REC", NULL } },
+ { -1, "ban exception new", { "BAN_REC", NULL } },
+ { -1, "ban exception remove", { "BAN_REC", NULL } },
+ { -1, "ban type changed", { "string", NULL } },
+ { -1, "commandlist new", { "COMMAND_REC", NULL } },
+ { -1, "commandlist remove", { "COMMAND_REC", NULL } },
+ { -1, "channel created", { "CHANNEL_REC", NULL } },
+ { -1, "channel destroyed", { "CHANNEL_REC", NULL } },
+ { -1, "channel name changed", { "CHANNEL_REC", NULL } },
+ { -1, "channel topic changed", { "CHANNEL_REC", NULL } },
+ { -1, "channel server changed", { "CHANNEL_REC", "Irssi::Server", NULL } },
+ { -1, "channel query", { "CHANNEL_REC", NULL } },
+ { -1, "channel wholist", { "CHANNEL_REC", NULL } },
+ { -1, "channel sync", { "CHANNEL_REC", NULL } },
+ { -1, "ctcp msg ", { "string", "SERVER_REC", "string", "string", "string", NULL } },
+ { -1, "default ctcp msg", { "string", "SERVER_REC", "string", "string", "string", NULL } },
+ { -1, "ctcp reply ", { "string", "SERVER_REC", "string", "string", "string", NULL } },
+ { -1, "default ctcp reply", { "string", "SERVER_REC", "string", "string", "string", NULL } },
+ { -1, "server lag", { "SERVER_REC", NULL } },
+ { -1, "server lag disconnect", { "SERVER_REC", NULL } },
+ { -1, "lag", { "string", "int", NULL } },
+ { -1, "invitelist new", { "CHANNEL_REC", "string", NULL } },
+ { -1, "invitelist remove", { "CHANNEL_REC", "string", NULL } },
+ { -1, "channel mode changed", { "CHANNEL_REC", NULL } },
+ { -1, "user mode changed", { "SERVER_REC", NULL } },
+ { -1, "nick mode changed", { "CHANNEL_REC", "NICK_REC", NULL } },
+ { -1, "netsplit add", { "NETSPLIT_REC", NULL } },
+ { -1, "netsplit remove", { "NETSPLIT_REC", NULL } },
+ { -1, "nicklist new", { "CHANNEL_REC", "NICK_REC", NULL } },
+ { -1, "nicklist remove", { "CHANNEL_REC", "NICK_REC", NULL } },
+ { -1, "nicklist changed", { "CHANNEL_REC", "NICK_REC", "string", NULL } },
+ { -1, "nick gone changed", { "CHANNEL_REC", "NICK_REC", NULL } },
+ { -1, "nick ircop changed", { "CHANNEL_REC", "NICK_REC", NULL } },
+ { -1, "server nick changed", { "SERVER_REC", NULL } },
+ { -1, "massjoin", { "CHANNEL_REC", "gslist_NICK_REC", NULL } },
+ { -1, "rawlog", { "SERVER_REC", "string", NULL } },
+ { -1, "server connect failed", { "SERVER_REC", NULL } },
+ { -1, "server connected", { "SERVER_REC", NULL } },
+ { -1, "server connecting", { "SERVER_REC", "ulongptr", NULL } },
+ { -1, "server looking", { "SERVER_REC", NULL } },
+ { -1, "server disconnected", { "SERVER_REC", NULL } },
+ { -1, "event connected", { "SERVER_REC", NULL } },
+ { -1, "server reconnect new", { "RECONNECT_REC", NULL } },
+ { -1, "server reconnect remove", { "RECONNECT_REC", NULL } },
+ { -1, "server reconnect not found", { "string", NULL } },
+ { -1, "print text stripped", { "SERVER_REC", "string", "int", "string", NULL } },
+ { -1, "dcc ctcp ", { "string", "DCC_REC", NULL } },
+ { -1, "default dcc ctcp", { "string", "DCC_REC", NULL } },
+ { -1, "dcc unknown ctcp", { "string", "string", "string", NULL } },
+ { -1, "dcc reply ", { "string", "DCC_REC", NULL } },
+ { -1, "default dcc reply", { "string", "DCC_REC", NULL } },
+ { -1, "dcc unknown reply", { "string", "string", "string", NULL } },
+ { -1, "dcc chat message", { "DCC_REC", "string", NULL } },
+ { -1, "dcc created", { "DCC_REC", NULL } },
+ { -1, "dcc destroyed", { "DCC_REC", NULL } },
+ { -1, "dcc connected", { "DCC_REC", NULL } },
+ { -1, "dcc rejecting", { "DCC_REC", NULL } },
+ { -1, "dcc closed", { "DCC_REC", NULL } },
+ { -1, "dcc chat message", { "DCC_REC", "string", NULL } },
+ { -1, "dcc transfer update", { "DCC_REC", NULL } },
+ { -1, "dcc request", { "DCC_REC", NULL } },
+ { -1, "dcc get receive", { "DCC_REC", NULL } },
+ { -1, "dcc error connect", { "DCC_REC", NULL } },
+ { -1, "dcc error file create", { "DCC_REC", "string", NULL } },
+ { -1, "dcc error file not found", { "string", "string", NULL } },
+ { -1, "dcc error get not found", { "string", NULL } },
+ { -1, "dcc error send exists", { "string", "string", NULL } },
+ { -1, "dcc error unknown type", { "string", NULL } },
+ { -1, "dcc error close not found", { "string", "string", "string", NULL } },
+ { -1, "flood", { "SERVER_REC", "string", "string", "string", "string", NULL } },
+ { -1, "autoignore new", { "SERVER_REC", "AUTOIGNORE_REC", NULL } },
+ { -1, "autoignore remove", { "SERVER_REC", "AUTOIGNORE_REC", NULL } },
+ { -1, "log new", { "LOG_REC", NULL } },
+ { -1, "log remove", { "LOG_REC", NULL } },
+ { -1, "log locked", { "LOG_REC", NULL } },
+ { -1, "log started", { "LOG_REC", NULL } },
+ { -1, "log stopped", { "LOG_REC", NULL } },
+ { -1, "log written", { "LOG_REC", "string", NULL } },
+ { -1, "notifylist new", { "NOTIFYLIST_REC", NULL } },
+ { -1, "notifylist remove", { "NOTIFYLIST_REC", NULL } },
+ { -1, "notifylist joined", { "SERVER_REC", "string", "string", "string", "string", "string", NULL } },
+ { -1, "notifylist away changed", { "SERVER_REC", "string", "string", "string", "string", "string", NULL } },
+ { -1, "notifylist unidle", { "SERVER_REC", "string", "string", "string", "string", "string", NULL } },
+ { -1, "notifylist left", { "SERVER_REC", "string", "string", "string", "string", "string", NULL } },
+ { -1, "plugin created", { "PLUGIN_REC", NULL } },
+ { -1, "plugin loaded", { "PLUGIN_REC", NULL } },
+ { -1, "plugin destroyed", { "PLUGIN_REC", NULL } },
+ { -1, "plugin error already loaded", { "string", NULL } },
+ { -1, "plugin error invalid", { "string", NULL } },
+ { -1, "plugin error load", { "string", "string", NULL } },
+ { -1, "plugin error not loaded", { "string", NULL } },
+ { -1, "plugin error version", { "string", NULL } },
+
+ { -1, NULL }
+};
diff --git a/src/perl/xs/Irssi.pm b/src/perl/xs/Irssi.pm
new file mode 100644
index 00000000..bd6af5fb
--- /dev/null
+++ b/src/perl/xs/Irssi.pm
@@ -0,0 +1,21 @@
+#
+# Perl interface to irssi functions.
+#
+
+package Irssi;
+
+use strict;
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
+
+$VERSION = "0.10";
+
+require Exporter;
+require DynaLoader;
+
+@ISA = qw(Exporter DynaLoader);
+@EXPORT = qw(channel_find_any);
+@EXPORT_OK = qw();
+bootstrap Irssi $VERSION;
+
+1;
+
diff --git a/src/perl/xs/Irssi.xs b/src/perl/xs/Irssi.xs
new file mode 100644
index 00000000..95c75163
--- /dev/null
+++ b/src/perl/xs/Irssi.xs
@@ -0,0 +1,933 @@
+/* This could be split to different files / modules ..? */
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+
+#undef _
+#include <irssi-plugin.h>
+#include <irc-base/server-setup.h>
+#include <irc-base/server-reconnect.h>
+#include <irc-base/server-redirect.h>
+
+typedef SERVER_REC *Irssi__Server;
+typedef SERVER_CONNECT_REC *Irssi__Connect;
+typedef RECONNECT_REC *Irssi__Reconnect;
+typedef CHANNEL_REC *Irssi__Channel;
+typedef COMMAND_REC *Irssi__Command;
+typedef NICK_REC *Irssi__Nick;
+typedef BAN_REC *Irssi__Ban;
+typedef DCC_REC *Irssi__Dcc;
+typedef NETSPLIT_REC *Irssi__Netsplit;
+typedef AUTOIGNORE_REC *Irssi__Autoignore;
+typedef LOG_REC *Irssi__Log;
+typedef LOG_ITEM_REC *Irssi__Logitem;
+typedef PLUGIN_REC *Irssi__Plugin;
+
+#define new_pv(a) (newSVpv((a) == NULL ? "" : (a), (a) == NULL ? 0 : strlen(a)))
+
+void add_connect_hash(HV *hv, SERVER_CONNECT_REC *conn)
+{
+ hv_store(hv, "address", 7, new_pv(conn->address), 0);
+ hv_store(hv, "port", 4, newSViv(conn->port), 0);
+ hv_store(hv, "password", 8, new_pv(conn->password), 0);
+
+ hv_store(hv, "ircnet", 6, new_pv(conn->ircnet), 0);
+ hv_store(hv, "wanted_nick", 11, new_pv(conn->nick), 0);
+ hv_store(hv, "alternate_nick", 14, new_pv(conn->alternate_nick), 0);
+ hv_store(hv, "username", 8, new_pv(conn->username), 0);
+ hv_store(hv, "realname", 8, new_pv(conn->realname), 0);
+ hv_store(hv, "autojoin_channels", 17, new_pv(conn->autojoin_channels), 0);
+}
+
+MODULE = Irssi PACKAGE = Irssi
+
+PROTOTYPES: ENABLE
+
+Irssi::Channel
+cur_channel()
+CODE:
+ RETVAL = cur_channel;
+OUTPUT:
+ RETVAL
+
+Irssi::Server
+cur_server()
+CODE:
+ RETVAL = cur_channel->server;
+OUTPUT:
+ RETVAL
+
+void
+channels()
+PREINIT:
+ GSList *tmp;
+ HV *stash;
+PPCODE:
+ stash = gv_stashpv("Irssi::Channel", 0);
+ for (tmp = channels; tmp != NULL; tmp = tmp->next) {
+ XPUSHs(sv_2mortal(sv_bless(newRV_noinc(newSViv(GPOINTER_TO_INT(tmp->data))), stash)));
+ }
+
+void
+servers()
+PREINIT:
+ GSList *tmp;
+ HV *stash;
+PPCODE:
+ stash = gv_stashpv("Irssi::Server", 0);
+ for (tmp = servers; tmp != NULL; tmp = tmp->next) {
+ XPUSHs(sv_2mortal(sv_bless(newRV_noinc(newSViv(GPOINTER_TO_INT(tmp->data))), stash)));
+ }
+
+void
+commands()
+PREINIT:
+ GSList *tmp;
+ HV *stash;
+PPCODE:
+ stash = gv_stashpv("Irssi::Command", 0);
+ for (tmp = commands; tmp != NULL; tmp = tmp->next) {
+ XPUSHs(sv_2mortal(sv_bless(newRV_noinc(newSViv(GPOINTER_TO_INT(tmp->data))), stash)));
+ }
+
+void
+dccs()
+PREINIT:
+ GSList *tmp;
+ HV *stash;
+PPCODE:
+ stash = gv_stashpv("Irssi::Dcc", 0);
+ for (tmp = dcc_conns; tmp != NULL; tmp = tmp->next) {
+ XPUSHs(sv_2mortal(sv_bless(newRV_noinc(newSViv(GPOINTER_TO_INT(tmp->data))), stash)));
+ }
+
+void
+logs()
+PREINIT:
+ GSList *tmp;
+ HV *stash;
+PPCODE:
+ stash = gv_stashpv("Irssi::Log", 0);
+ for (tmp = logs; tmp != NULL; tmp = tmp->next) {
+ XPUSHs(sv_2mortal(sv_bless(newRV_noinc(newSViv(GPOINTER_TO_INT(tmp->data))), stash)));
+ }
+
+void
+plugins()
+PREINIT:
+ GSList *tmp;
+ HV *stash;
+PPCODE:
+ stash = gv_stashpv("Irssi::Plugin", 0);
+ for (tmp = plugins; tmp != NULL; tmp = tmp->next) {
+ XPUSHs(sv_2mortal(sv_bless(newRV_noinc(newSViv(GPOINTER_TO_INT(tmp->data))), stash)));
+ }
+
+Irssi::Connect
+server_create_conn(dest, port=6667, password=NULL, nick=NULL, channels=NULL)
+ char *dest
+ int port
+ char *password
+ char *nick
+ char *channels
+
+Irssi::Server
+server_find_tag(tag)
+ char *tag
+
+Irssi::Server
+server_find_ircnet(ircnet)
+ char *ircnet
+
+Irssi::Channel
+channel_find(channel)
+ char *channel
+CODE:
+ RETVAL = channel_find(NULL, channel);
+OUTPUT:
+ RETVAL
+
+void
+print(str)
+ char *str
+CODE:
+ printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE, str);
+
+void
+signal_emit(signal, ...)
+ char *signal
+CODE:
+ void *p[6];
+ int n;
+
+ memset(p, 0, sizeof(p));
+ for (n = 1; n < items && n < 6; n++) {
+ p[n-1] = SvPOKp(ST(n)) ? SvPV(ST(n), PL_na) : (void *) SvIV((SV*)SvRV(ST(n)));
+ }
+ signal_emit(signal, items-1, p[0], p[1], p[2], p[3], p[4], p[5]);
+
+void
+signal_add(signal, func)
+ char *signal
+ char *func
+CODE:
+ perl_signal_add(signal, func);
+
+void
+signal_add_last(signal, func)
+ char *signal
+ char *func
+CODE:
+ perl_signal_add_last(signal, func);
+
+void
+signal_remove(signal, func)
+ char *signal
+ char *func
+CODE:
+ perl_signal_remove(signal, func);
+
+int
+timeout_add(msecs, func, data)
+ int msecs
+ char *func
+ char *data
+CODE:
+ RETVAL = perl_timeout_add(msecs, func, data);
+OUTPUT:
+ RETVAL
+
+void
+timeout_remove(tag)
+ int tag
+CODE:
+ perl_timeout_remove(tag);
+
+void
+command_bind(cmd, category, func)
+ char *cmd
+ char *category
+ char *func
+CODE:
+ char *signal;
+
+ command_bind(cmd, *category ? category : "Perl scripts' commands", NULL);
+ signal = g_strconcat("command ", cmd, NULL);
+ perl_signal_add(signal, func);
+ g_free(signal);
+
+void
+command_unbind(cmd, func)
+ char *cmd
+ char *func
+CODE:
+ char *signal;
+
+ command_unbind(cmd, NULL);
+ signal = g_strconcat("command ", cmd, NULL);
+ perl_signal_remove(signal, func);
+ g_free(signal);
+
+void
+command(cmd, server=cur_channel->server, channel=cur_channel)
+ char *cmd
+ Irssi::Server server
+ Irssi::Channel channel
+CODE:
+ signal_emit("send command", 3, cmd, server, channel);
+
+int
+is_channel(text)
+ char *text
+CODE:
+ RETVAL = ischannel(*text);
+OUTPUT:
+ RETVAL
+
+int
+irc_mask_match(mask, nick, user, host)
+ char *mask
+ char *nick
+ char *user
+ char *host
+
+int
+irc_mask_match_address(mask, nick, address)
+ char *mask
+ char *nick
+ char *address
+
+int
+irc_masks_match(masks, nick, address)
+ char *masks
+ char *nick
+ char *address
+
+char *
+irc_get_mask(nick, host, flags)
+ char *nick
+ char *host
+ int flags
+
+int
+level2bits(str)
+ char *str
+
+char *
+bits2level(bits)
+ int bits
+
+int
+combine_level(level, str)
+ int level
+ char *str
+
+Irssi::Dcc
+dcc_find_item(type, nick, arg)
+ int type
+ char *nick
+ char *arg
+
+Irssi::Dcc
+dcc_find_by_port(nick, port)
+ char *nick
+ int port
+
+char *
+dcc_type2str(type)
+ int type
+
+int
+dcc_str2type(type)
+ char *type
+
+void
+ignore_add(mask, level)
+ char *mask
+ char *level
+
+void
+ignore_remove(mask, level)
+ char *mask
+ char *level
+
+Irssi::Log
+log_create(fname, data)
+ char *fname
+ char *data
+
+Irssi::Log
+log_create_with_level(fname, level)
+ char *fname
+ int level
+
+Irssi::Log
+log_file_find(fname)
+ char *fname
+
+void
+notifylist_add(nick, ircnet)
+ char *nick
+ char *ircnet
+
+Irssi::Server
+notifylist_ison(nick, serverlist)
+ char *nick
+ char *serverlist
+
+int
+plugin_load(name, args)
+ char *name
+ char *args
+
+char *
+plugin_get_description(name)
+ char *name
+
+Irssi::Plugin
+plugin_find(name)
+ char *name
+
+void
+setup_get(option)
+ char *option
+PREINIT:
+ char *ret;
+PPCODE:
+ switch(setup_option_type(option)) {
+ case SETUP_TYPE_TOGGLEBUTTON:
+ XPUSHs(sv_2mortal(newSViv(setup_get_bool(option))));
+ break;
+ case SETUP_TYPE_SPIN:
+ case SETUP_TYPE_INT_OBJECT:
+ XPUSHs(sv_2mortal(newSViv(setup_get_int(option))));
+ break;
+ case SETUP_TYPE_ENTRY:
+ ret = setup_get_str(option);
+ XPUSHs(sv_2mortal(newSVpv(ret, strlen(ret))));
+ break;
+ }
+
+
+#*******************************************************
+MODULE = Irssi PACKAGE = Irssi::Server PREFIX = server_
+#*******************************************************
+
+void
+send_raw(server, cmd)
+ Irssi::Server server
+ char *cmd
+CODE:
+ irc_send_cmd(server, cmd);
+
+void
+command(server, cmd, channel=cur_channel)
+ char *cmd
+ Irssi::Server server
+ Irssi::Channel channel
+CODE:
+ if (channel->server != server) {
+ GSList *tmp;
+
+ for (tmp = channels; tmp != NULL; tmp = tmp->next) {
+ CHANNEL_REC *rec = tmp->data;
+
+ if (rec->server == server) {
+ channel = rec;
+ break;
+ }
+ }
+ }
+ signal_emit("send command", 3, cmd, server, channel);
+
+void
+server_disconnect(server)
+ Irssi::Server server
+
+Irssi::Channel
+channel_create(server, channel, type, automatic)
+ Irssi::Server server
+ char *channel
+ int type
+ int automatic
+
+Irssi::Channel
+channel_find(server, channel)
+ Irssi::Server server
+ char *channel
+
+Irssi::Channel
+channel_find_closest(server, channel, level)
+ Irssi::Server server
+ char *channel
+ int level
+
+Irssi::Channel
+channel_find_level(server, level)
+ Irssi::Server server
+ int level
+
+void
+printtext(server, channel, level, str)
+ Irssi::Server server
+ char *channel
+ int level
+ char *str
+CODE:
+ printtext(server, channel, level, str);
+
+void
+irc_send_cmd_split(server, cmd, arg, max_nicks)
+ Irssi::Server server
+ char *cmd
+ int arg
+ int max_nicks
+
+void
+ctcp_send_reply(server, data)
+ Irssi::Server server
+ char *data
+
+Irssi::Netsplit
+netsplit_find(server, nick, address)
+ Irssi::Server server
+ char *nick
+ char *address
+
+Irssi::Nick
+netsplit_find_channel(server, nick, address, channel)
+ Irssi::Server server
+ char *nick
+ char *address
+ char *channel
+
+void
+rawlog_input(server, str)
+ Irssi::Server server
+ char *str
+
+void
+rawlog_output(server, str)
+ Irssi::Server server
+ char *str
+
+void
+rawlog_redirect(server, str)
+ Irssi::Server server
+ char *str
+
+void
+server_redirect_init(server, command, last, ...)
+ Irssi::Server server
+ char *command
+ int last
+PREINIT:
+ GSList *list;
+ int n;
+CODE:
+ list = NULL;
+ for (n = 3; n < items; n++) {
+ list = g_slist_append(list, SvPV(ST(n), PL_na));
+ }
+ server_redirect_initv(server, command, last, list);
+
+int
+server_redirect_single_event(server, arg, last, group, event, signal, argpos)
+ Irssi::Server server
+ char *arg
+ int last
+ int group
+ char *event
+ char *signal
+ int argpos
+
+void
+server_redirect_event(server, arg, last, ...)
+ Irssi::Server server
+ char *arg
+ int last
+PREINIT:
+ int n, group;
+CODE:
+ group = 0;
+ for (n = 3; n+3 <= items; n += 3, last--) {
+ group = server_redirect_single_event(server, arg, last > 0, group,
+ (char *) SvPV(ST(n), PL_na), (char *) SvPV(ST(n+1), PL_na), (int) SvIV(ST(n+2)));
+ }
+
+void
+autoignore_add(server, type, nick)
+ Irssi::Server server
+ int type
+ char *nick
+
+int
+autoignore_remove(server, mask, level)
+ Irssi::Server server
+ char *mask
+ char *level
+
+int
+ignore_check(server, nick, host, type)
+ Irssi::Server server
+ char *nick
+ char *host
+ int type
+
+int
+notifylist_ison_server(server, nick)
+ Irssi::Server server
+ char *nick
+
+void
+values(server)
+ Irssi::Server server
+PREINIT:
+ HV *hv;
+PPCODE:
+ hv = newHV();
+ add_connect_hash(hv, server->connrec);
+ hv_store(hv, "real_address", 12, new_pv(server->real_address), 0);
+ hv_store(hv, "tag", 3, new_pv(server->tag), 0);
+ hv_store(hv, "nick", 4, new_pv(server->nick), 0);
+ hv_store(hv, "usermode", 8, new_pv(server->usermode), 0);
+ hv_store(hv, "usermode_away", 13, newSViv(server->usermode_away), 0);
+ hv_store(hv, "away_reason", 11, new_pv(server->away_reason), 0);
+ hv_store(hv, "connected", 9, newSViv(server->connected), 0);
+ hv_store(hv, "connection_lost", 15, newSViv(server->connection_lost), 0);
+ XPUSHs(sv_2mortal(newRV_noinc((SV*)hv)));
+
+#*******************************************************
+MODULE = Irssi PACKAGE = Irssi::Connect PREFIX = server_
+#*******************************************************
+
+void
+values(conn)
+ Irssi::Connect conn
+PREINIT:
+ HV *hv;
+PPCODE:
+ hv = newHV();
+ add_connect_hash(hv, conn);
+ XPUSHs(sv_2mortal(newRV_noinc((SV*)hv)));
+
+Irssi::Server
+server_connect(conn)
+ Irssi::Connect conn
+
+#*******************************************************
+MODULE = Irssi PACKAGE = Irssi::Reconnect
+#*******************************************************
+
+void
+values(reconnect)
+ Irssi::Reconnect reconnect
+PREINIT:
+ HV *hv;
+PPCODE:
+ hv = newHV();
+ add_connect_hash(hv, reconnect->conn);
+ hv_store(hv, "tag", 3, newSViv(reconnect->tag), 0);
+ hv_store(hv, "next_connect", 12, newSViv(reconnect->next_connect), 0);
+ XPUSHs(sv_2mortal(newRV_noinc((SV*)hv)));
+
+#*******************************************************
+MODULE = Irssi PACKAGE = Irssi::Channel PREFIX = channel_
+#*******************************************************
+
+void
+command(channel, cmd)
+ Irssi::Channel channel
+ char *cmd
+CODE:
+ signal_emit("send command", 3, cmd, channel->server, channel);
+
+void
+channel_destroy(channel)
+ Irssi::Channel channel
+
+void
+channel_change_name(channel, name)
+ Irssi::Channel channel
+ char *name
+
+char *
+channel_get_mode(channel)
+ Irssi::Channel channel
+
+Irssi::Nick
+nicklist_insert(channel, nick, op, voice, send_massjoin)
+ Irssi::Channel channel
+ char *nick
+ int op
+ int voice
+ int send_massjoin
+
+void
+nicklist_remove(channel, nick)
+ Irssi::Channel channel
+ Irssi::Nick nick
+
+Irssi::Nick
+nicklist_find(channel, mask)
+ Irssi::Channel channel
+ char *mask
+
+void
+nicklist_getnicks(channel)
+ Irssi::Channel channel
+PREINIT:
+ GSList *list, *tmp;
+ HV *stash;
+PPCODE:
+ list = nicklist_getnicks(channel);
+
+ stash = gv_stashpv("Irssi::Nick", 0);
+ for (tmp = list; tmp != NULL; tmp = tmp->next) {
+ XPUSHs(sv_2mortal(sv_bless(newRV_noinc(newSViv(GPOINTER_TO_INT(tmp->data))), stash)));
+ }
+ g_slist_free(list);
+
+Irssi::Ban
+ban_add(channel, ban, nick, time)
+ Irssi::Channel channel
+ char *ban
+ char *nick
+ time_t time
+
+void
+ban_remove(channel, ban)
+ Irssi::Channel channel
+ char *ban
+
+Irssi::Ban
+ban_exception_add(channel, ban, nick, time)
+ Irssi::Channel channel
+ char *ban
+ char *nick
+ time_t time
+
+void
+ban_exception_remove(channel, ban)
+ Irssi::Channel channel
+ char *ban
+
+char *
+ban_get_mask(channel, nick)
+ Irssi::Channel channel
+ char *nick
+
+void
+modes_set(channel, data, mode)
+ Irssi::Channel channel
+ char *data
+ char *mode
+CODE:
+ modes_set(data, mode, channel->server, channel);
+
+void
+modes_parse_channel(channel, setby, modestr)
+ Irssi::Channel channel
+ char *setby
+ char *modestr
+
+void
+invitelist_add(channel, mask)
+ Irssi::Channel channel
+ char *mask
+
+void
+invitelist_remove(channel, mask)
+ Irssi::Channel channel
+ char *mask
+
+void
+values(channel)
+ Irssi::Channel channel
+PREINIT:
+ HV *hv, *stash;
+ char *type;
+PPCODE:
+ switch (channel->type)
+ {
+ case CHANNEL_TYPE_CHANNEL:
+ type = "channel";
+ break;
+ case CHANNEL_TYPE_QUERY:
+ type = "query";
+ break;
+ case CHANNEL_TYPE_DCC_CHAT:
+ type = "dcc chat";
+ break;
+ default:
+ type = "empty";
+ break;
+ }
+ hv = newHV();
+ stash = gv_stashpv("Irssi::Server", 0);
+ hv_store(hv, "server", 6, sv_bless(newRV_noinc(newSViv(GPOINTER_TO_INT(channel->server))), stash), 0);
+
+ hv_store(hv, "name", 4, new_pv(channel->name), 0);
+ hv_store(hv, "type", 4, new_pv(type), 0);
+
+ hv_store(hv, "topic", 5, new_pv(channel->topic), 0);
+ hv_store(hv, "key", 3, new_pv(channel->key), 0);
+ hv_store(hv, "limit", 5, newSViv(channel->limit), 0);
+
+ hv_store(hv, "level", 5, newSViv(channel->level), 0);
+ hv_store(hv, "new_data", 8, newSViv(channel->new_data), 0);
+
+ hv_store(hv, "synced", 6, newSViv(channel->synced), 0);
+ hv_store(hv, "wholist", 7, newSViv(channel->wholist), 0);
+ hv_store(hv, "names_got", 9, newSViv(channel->names_got), 0);
+ hv_store(hv, "chanop", 6, newSViv(channel->chanop), 0);
+ hv_store(hv, "left", 4, newSViv(channel->left), 0);
+ hv_store(hv, "kicked", 6, newSViv(channel->kicked), 0);
+
+ XPUSHs(sv_2mortal(newRV_noinc((SV*)hv)));
+
+#*******************************************************
+MODULE = Irssi PACKAGE = Irssi::Command
+#*******************************************************
+
+void
+values(cmd)
+ Irssi::Command cmd
+PREINIT:
+ HV *hv;
+PPCODE:
+ hv = newHV();
+ hv_store(hv, "cmd", 3, new_pv(cmd->cmd), 0);
+ hv_store(hv, "category", 8, new_pv(cmd->category), 0);
+ XPUSHs(sv_2mortal(newRV_noinc((SV*)hv)));
+
+#*******************************************************
+MODULE = Irssi PACKAGE = Irssi::Nick
+#*******************************************************
+
+void
+values(nick)
+ Irssi::Nick nick
+PREINIT:
+ HV *hv;
+PPCODE:
+ hv = newHV();
+ hv_store(hv, "nick", 4, new_pv(nick->nick), 0);
+ hv_store(hv, "host", 4, new_pv(nick->host), 0);
+ hv_store(hv, "name", 4, new_pv(nick->realname), 0);
+ hv_store(hv, "hops", 4, newSViv(nick->hops), 0);
+ hv_store(hv, "op", 2, newSViv(nick->op), 0);
+ hv_store(hv, "voice", 5, newSViv(nick->voice), 0);
+ hv_store(hv, "gone", 4, newSViv(nick->gone), 0);
+ hv_store(hv, "ircop", 5, newSViv(nick->ircop), 0);
+ hv_store(hv, "last_check", 10, newSViv(nick->last_check), 0);
+ hv_store(hv, "send_massjoin", 13, newSViv(nick->send_massjoin), 0);
+ XPUSHs(sv_2mortal(newRV_noinc((SV*)hv)));
+
+#*******************************************************
+MODULE = Irssi PACKAGE = Irssi::Ban
+#*******************************************************
+
+void
+values(ban)
+ Irssi::Ban ban
+PREINIT:
+ HV *hv;
+PPCODE:
+ hv = newHV();
+ hv_store(hv, "ban", 3, new_pv(ban->ban), 0);
+ hv_store(hv, "setby", 5, new_pv(ban->setby), 0);
+ hv_store(hv, "time", 4, newSViv(ban->time), 0);
+ XPUSHs(sv_2mortal(newRV_noinc((SV*)hv)));
+
+#*******************************************************
+MODULE = Irssi PACKAGE = Irssi::Dcc PREFIX = dcc_
+#*******************************************************
+
+void
+dcc_destroy(dcc)
+ Irssi::Dcc dcc
+
+void
+values(ban)
+ Irssi::Ban ban
+PREINIT:
+ HV *hv;
+PPCODE:
+ hv = newHV();
+ hv_store(hv, "ban", 3, new_pv(ban->ban), 0);
+ hv_store(hv, "setby", 5, new_pv(ban->setby), 0);
+ hv_store(hv, "time", 4, newSViv(ban->time), 0);
+ XPUSHs(sv_2mortal(newRV_noinc((SV*)hv)));
+
+#*******************************************************
+MODULE = Irssi PACKAGE = Irssi::Netsplit
+#*******************************************************
+
+void
+values(netsplit)
+ Irssi::Netsplit netsplit
+PREINIT:
+ HV *hv;
+PPCODE:
+ hv = newHV();
+ hv_store(hv, "nick", 4, new_pv(netsplit->nick), 0);
+ hv_store(hv, "address", 7, new_pv(netsplit->address), 0);
+ hv_store(hv, "server", 6, new_pv(netsplit->server), 0);
+ hv_store(hv, "destserver", 10, new_pv(netsplit->destserver), 0);
+ hv_store(hv, "destroy", 7, newSViv(netsplit->destroy), 0);
+ /*FIXME: add GSList *channels;*/
+ XPUSHs(sv_2mortal(newRV_noinc((SV*)hv)));
+
+#*******************************************************
+MODULE = Irssi PACKAGE = Irssi::Autoignore
+#*******************************************************
+
+void
+values(ai)
+ Irssi::Autoignore ai
+PREINIT:
+ HV *hv;
+PPCODE:
+ hv = newHV();
+ hv_store(hv, "nick", 4, new_pv(ai->nick), 0);
+ hv_store(hv, "timeleft", 8, newSViv(ai->timeleft), 0);
+ hv_store(hv, "level", 5, newSViv(ai->level), 0);
+ XPUSHs(sv_2mortal(newRV_noinc((SV*)hv)));
+
+#*******************************************************
+MODULE = Irssi PACKAGE = Irssi::Log PREFIX = log_
+#*******************************************************
+
+void
+values(log)
+ Irssi::Log log
+PREINIT:
+ HV *hv;
+PPCODE:
+ hv = newHV();
+ hv_store(hv, "fname", 5, new_pv(log->fname), 0);
+ hv_store(hv, "autoopen_log", 12, newSViv(log->autoopen_log), 0);
+ hv_store(hv, "last", 4, newSViv(log->last), 0);
+ hv_store(hv, "level", 5, newSViv(log->level), 0);
+ /*FIXME: add GSList *items;*/
+ XPUSHs(sv_2mortal(newRV_noinc((SV*)hv)));
+
+void
+destroy(log)
+ Irssi::Log log
+CODE:
+ log_file_destroy(log);
+
+int
+open(log)
+ Irssi::Log log
+CODE:
+ log_file_open(log);
+
+void
+close(log)
+ Irssi::Log log
+CODE:
+ log_file_close(log);
+
+void
+log_append_item(log, name, level)
+ Irssi::Log log
+ char *name
+ int level
+
+void
+log_remove_item(log, name)
+ Irssi::Log log
+ char *name
+
+#*******************************************************
+MODULE = Irssi PACKAGE = Irssi::Logitem
+#*******************************************************
+
+void
+values(item)
+ Irssi::Logitem item
+PREINIT:
+ HV *hv;
+PPCODE:
+ hv = newHV();
+ hv_store(hv, "name", 4, new_pv(item->name), 0);
+ hv_store(hv, "level", 5, newSViv(item->level), 0);
+ XPUSHs(sv_2mortal(newRV_noinc((SV*)hv)));
+
+#*******************************************************
+MODULE = Irssi PACKAGE = Irssi::Plugin PREFIX = plugin_
+#*******************************************************
+
+void
+values(plugin)
+ Irssi::Plugin plugin
+PREINIT:
+ HV *hv;
+PPCODE:
+ hv = newHV();
+ hv_store(hv, "name", 4, new_pv(plugin->name), 0);
+ hv_store(hv, "description", 11, new_pv(plugin->description), 0);
+ XPUSHs(sv_2mortal(newRV_noinc((SV*)hv)));
diff --git a/src/perl/xs/Makefile.PL.in b/src/perl/xs/Makefile.PL.in
new file mode 100644
index 00000000..430d59db
--- /dev/null
+++ b/src/perl/xs/Makefile.PL.in
@@ -0,0 +1,6 @@
+use ExtUtils::MakeMaker;
+
+WriteMakefile('NAME' => 'Irssi',
+ 'LIBS' => '',
+ 'INC' => '-I@top_srcdir@/src @GLIB_CFLAGS@',
+ 'VERSION_FROM' => '@srcdir@/Irssi.pm');
diff --git a/src/perl/xs/typemap b/src/perl/xs/typemap
new file mode 100644
index 00000000..ba1bc85c
--- /dev/null
+++ b/src/perl/xs/typemap
@@ -0,0 +1,14 @@
+TYPEMAP
+Irssi::Server T_PTROBJ
+Irssi::Connect T_PTROBJ
+Irssi::Reconnect T_PTROBJ
+Irssi::Channel T_PTROBJ
+Irssi::Command T_PTROBJ
+Irssi::Nick T_PTROBJ
+Irssi::Ban T_PTROBJ
+Irssi::Dcc T_PTROBJ
+Irssi::Netsplit T_PTROBJ
+Irssi::Autoignore T_PTROBJ
+Irssi::Log T_PTROBJ
+Irssi::Logitem T_PTROBJ
+Irssi::Plugin T_PTROBJ
diff --git a/src/perl/xsinit.c b/src/perl/xsinit.c
new file mode 100644
index 00000000..590eddb7
--- /dev/null
+++ b/src/perl/xsinit.c
@@ -0,0 +1,41 @@
+#if defined(__cplusplus) && !defined(PERL_OBJECT)
+#define is_cplusplus
+#endif
+
+#ifdef is_cplusplus
+extern "C" {
+#endif
+
+#include <EXTERN.h>
+#include <perl.h>
+#ifdef PERL_OBJECT
+#define NO_XSLOCKS
+#include <XSUB.h>
+#include "win32iop.h"
+#include <fcntl.h>
+#include <perlhost.h>
+#endif
+#ifdef is_cplusplus
+}
+# ifndef EXTERN_C
+# define EXTERN_C extern "C"
+# endif
+#else
+# ifndef EXTERN_C
+# define EXTERN_C extern
+# endif
+#endif
+
+EXTERN_C void xs_init _((void));
+
+EXTERN_C void boot_DynaLoader _((CV* cv));
+
+EXTERN_C void
+xs_init(void)
+{
+ char *file = __FILE__;
+ dXSUB_SYS;
+
+ /* DynaLoader is a special case */
+ newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
+}