summary refs log tree commit diff stats
path: root/plugins/tcl
diff options
context:
space:
mode:
authorberkeviktor@aol.com <berkeviktor@aol.com>2011-02-24 04:14:30 +0100
committerberkeviktor@aol.com <berkeviktor@aol.com>2011-02-24 04:14:30 +0100
commit4a6ceffb98a0b785494f680d3776c4bfc4052f9e (patch)
tree850703c1c841ccd99f58d0b06084615aaebe782c /plugins/tcl
parentf16af8be941b596dedac3bf4e371ee2d21f4b598 (diff)
add xchat r1489
Diffstat (limited to 'plugins/tcl')
-rw-r--r--plugins/tcl/Makefile.am10
-rw-r--r--plugins/tcl/README55
-rw-r--r--plugins/tcl/printevents.h319
-rw-r--r--plugins/tcl/tclplugin.c2290
-rw-r--r--plugins/tcl/tclplugin.h98
5 files changed, 2772 insertions, 0 deletions
diff --git a/plugins/tcl/Makefile.am b/plugins/tcl/Makefile.am
new file mode 100644
index 00000000..804fc83b
--- /dev/null
+++ b/plugins/tcl/Makefile.am
@@ -0,0 +1,10 @@
+
+EXTRA_DIST=printevents.h tclplugin.h README
+
+libdir = $(xchatlibdir)/plugins
+
+lib_LTLIBRARIES = tcl.la
+tcl_la_SOURCES = tclplugin.c
+tcl_la_LDFLAGS = -avoid-version -module 
+tcl_la_LIBADD = $(TCL_LIBS)
+INCLUDES = $(TCL_CFLAGS) $(COMMON_CFLAGS) -I$(srcdir)/..
diff --git a/plugins/tcl/README b/plugins/tcl/README
new file mode 100644
index 00000000..e46635ef
--- /dev/null
+++ b/plugins/tcl/README
@@ -0,0 +1,55 @@
+Please read this document before asking questions.
+
+(1) WHAT IS THE TCL PLUGIN?
+
+    The XChat Tcl Plugin adds the complete Tcl scripting language to the
+    XChat 1.9.x and 2.x IRC client.  The design philosophy behind the tcl
+    plugin was to give all the power of the C API. yet completely shield
+    the user from all the complexities of it.  It is lightly modeled after
+    after Xircon; an old windows TCL enabled client with a little bit of
+    eggdrop functionality to it.
+
+    Features:
+     * Uses the popular TCL scripting language.
+     * Familiar to eggdrop bot owners.
+     * Adds many new XChat specific commands to the Tcl language for 
+       handling of events, user commands, timers, etc. 
+     * It's actually documented! (Hey, what a concept!) 
+     * Works with XChat 1.9.x and 2.x. 
+     * Open source (GPL) 
+
+    The supplied documentation for Tcl Plugin commands can be 
+    found in doc/tclplugin.html
+    
+    For a comprehensive list of IRC server tokens, see
+    doc/tokens.txt
+
+
+(2) HOW TO GET TCL PLUGIN
+
+    You can always find the latest version of the Tcl Plugin at:
+
+       http://www.scriptkitties.com/tclplugin
+
+   You must also have Tcl 8.3 or higher installed on your system.
+
+     Tcl can be obtained from:
+
+       http://sourceforge.net/projects/tcl
+       http://tcl.activestate.com (pre-compiled binaries)
+
+     Tcl Man Pages
+
+       http://tcl.activestate.com/man/
+
+     Tcl Tutorials:
+
+       http://hegel.ittc.ukans.edu/topics/tcltk/tutorial-noplugin/
+       http://jan.netcomp.monash.edu.au/ProgrammingUnix/tcl/tcl_tut.html
+       http://users.belgacom.net/bruno.champagne/tcl.html
+
+
+(3) QUICK STARTUP
+
+    SEE 'INSTALL'
+
diff --git a/plugins/tcl/printevents.h b/plugins/tcl/printevents.h
new file mode 100644
index 00000000..dfea4aea
--- /dev/null
+++ b/plugins/tcl/printevents.h
@@ -0,0 +1,319 @@
+/***************************************************************************
+                           printevents.h  -  TCL plugin header file
+                           -------------------------------------------------
+    begin                : Sat Nov  9 17:31:20 MST 2002
+    copyright            : Copyright 2002-2007 Daniel P. Stasinski
+    email                : mooooooo@avenues.org
+    generated            : Mon May 21 06:03:00 PM MST 2007
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   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.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+typedef struct {
+    char *event;
+    char *emit;
+    int argc;
+    xchat_hook *hook;
+} print_event;
+
+enum
+{
+	CHAT,
+	XC_APPFOCUS,
+	XC_TABOPEN,
+	XC_TABCLOSE,
+	XC_TABFOCUS,
+	XC_KEYPRESS,
+	XC_ADDNOTIFY,
+	XC_BANLIST,
+	XC_BANNED,
+	XC_BEEP,
+	XC_CHANGENICK,
+	XC_CHANACTION,
+	XC_HCHANACTION,
+	XC_CHANBAN,
+	XC_CHANDATE,
+	XC_CHANDEHOP,
+	XC_CHANDEOP,
+	XC_CHANDEVOICE,
+	XC_CHANEXEMPT,
+	XC_CHANHOP,
+	XC_CHANINVITE,
+	XC_CHANLISTHEAD,
+	XC_CHANMSG,
+	XC_CHANMODEGEN,
+	XC_CHANMODES,
+	XC_HCHANMSG,
+	XC_CHANNOTICE,
+	XC_CHANOP,
+	XC_CHANRMEXEMPT,
+	XC_CHANRMINVITE,
+	XC_CHANRMKEY,
+	XC_CHANRMLIMIT,
+	XC_CHANSETKEY,
+	XC_CHANSETLIMIT,
+	XC_CHANUNBAN,
+	XC_CHANVOICE,
+	XC_CONNECTED,
+	XC_CONNECT,
+	XC_CONNFAIL,
+	XC_CTCPGEN,
+	XC_CTCPGENC,
+	XC_CTCPSEND,
+	XC_CTCPSND,
+	XC_CTCPSNDC,
+	XC_DCCCHATABORT,
+	XC_DCCCONCHAT,
+	XC_DCCCHATF,
+	XC_DCCCHATOFFER,
+	XC_DCCCHATOFFERING,
+	XC_DCCCHATREOFFER,
+	XC_DCCCONFAIL,
+	XC_DCCGENERICOFFER,
+	XC_DCCHEAD,
+	XC_MALFORMED,
+	XC_DCCOFFER,
+	XC_DCCIVAL,
+	XC_DCCRECVABORT,
+	XC_DCCRECVCOMP,
+	XC_DCCCONRECV,
+	XC_DCCRECVERR,
+	XC_DCCFILEERR,
+	XC_DCCRENAME,
+	XC_DCCRESUMEREQUEST,
+	XC_DCCSENDABORT,
+	XC_DCCSENDCOMP,
+	XC_DCCCONSEND,
+	XC_DCCSENDFAIL,
+	XC_DCCSENDOFFER,
+	XC_DCCSTALL,
+	XC_DCCTOUT,
+	XC_DELNOTIFY,
+	XC_DISCON,
+	XC_FOUNDIP,
+	XC_GENMSG,
+	XC_IGNOREADD,
+	XC_IGNORECHANGE,
+	XC_IGNOREFOOTER,
+	XC_IGNOREHEADER,
+	XC_IGNOREREMOVE,
+	XC_IGNOREEMPTY,
+	XC_INVITE,
+	XC_INVITED,
+	XC_JOIN,
+	XC_KEYWORD,
+	XC_KICK,
+	XC_KILL,
+	XC_MSGSEND,
+	XC_MOTD,
+	XC_MOTDSKIP,
+	XC_NICKCLASH,
+	XC_NICKFAIL,
+	XC_NODCC,
+	XC_NOCHILD,
+	XC_NOTICE,
+	XC_NOTICESEND,
+	XC_NOTIFYEMPTY,
+	XC_NOTIFYHEAD,
+	XC_NOTIFYNUMBER,
+	XC_NOTIFYOFFLINE,
+	XC_NOTIFYONLINE,
+	XC_OPENDIALOG,
+	XC_PART,
+	XC_PARTREASON,
+	XC_PINGREP,
+	XC_PINGTIMEOUT,
+	XC_PRIVMSG,
+	XC_DPRIVMSG,
+	XC_ALREADYPROCESS,
+	XC_QUIT,
+	XC_RAWMODES,
+	XC_WALLOPS,
+	XC_RESOLVINGUSER,
+	XC_SERVERCONNECTED,
+	XC_SERVERERROR,
+	XC_SERVERLOOKUP,
+	XC_SERVNOTICE,
+	XC_SERVTEXT,
+	XC_STOPCONNECT,
+	XC_TOPIC,
+	XC_NEWTOPIC,
+	XC_TOPICDATE,
+	XC_UKNHOST,
+	XC_USERLIMIT,
+	XC_USERSONCHAN,
+	XC_WHOIS_AUTH,
+	XC_WHOIS5,
+	XC_WHOIS2,
+	XC_WHOIS6,
+	XC_WHOIS_ID,
+	XC_WHOIS4,
+	XC_WHOIS4T,
+	XC_WHOIS1,
+	XC_WHOIS_REALHOST,
+	XC_WHOIS3,
+	XC_WHOIS_SPECIAL,
+	XC_UJOIN,
+	XC_UKICK,
+	XC_UPART,
+	XC_UPARTREASON,
+	XC_UACTION,
+	XC_UINVITE,
+	XC_UCHANMSG,
+	XC_UCHANGENICK,
+	XC_SIZE
+};
+
+static print_event xc[] = {
+	{ "CHAT", "DCC Chat Text", -1, NULL },
+	{ "XC_APPFOCUS", "Focus Window", -3, NULL },
+	{ "XC_TABOPEN", "Open Context", -2, NULL },
+	{ "XC_TABCLOSE", "Close Context", -2, NULL },
+	{ "XC_TABFOCUS", "Focus Tab", -2, NULL },
+	{ "XC_KEYPRESS", "Key Press", 4, NULL },
+	{ "XC_ADDNOTIFY", "Add Notify", 1, NULL },
+	{ "XC_BANLIST", "Ban List", 4, NULL },
+	{ "XC_BANNED", "Banned", 1, NULL },
+	{ "XC_BEEP", "Beep", 0, NULL },
+	{ "XC_CHANGENICK", "Change Nick", 2, NULL },
+	{ "XC_CHANACTION", "Channel Action", 3, NULL },
+	{ "XC_HCHANACTION", "Channel Action Hilight", 3, NULL },
+	{ "XC_CHANBAN", "Channel Ban", 2, NULL },
+	{ "XC_CHANDATE", "Channel Creation", 2, NULL },
+	{ "XC_CHANDEHOP", "Channel DeHalfOp", 2, NULL },
+	{ "XC_CHANDEOP", "Channel DeOp", 2, NULL },
+	{ "XC_CHANDEVOICE", "Channel DeVoice", 2, NULL },
+	{ "XC_CHANEXEMPT", "Channel Exempt", 2, NULL },
+	{ "XC_CHANHOP", "Channel Half-Operator", 2, NULL },
+	{ "XC_CHANINVITE", "Channel INVITE", 2, NULL },
+	{ "XC_CHANLISTHEAD", "Channel List", 0, NULL },
+	{ "XC_CHANMSG", "Channel Message", 4, NULL },
+	{ "XC_CHANMODEGEN", "Channel Mode Generic", 4, NULL },
+	{ "XC_CHANMODES", "Channel Modes", 2, NULL },
+	{ "XC_HCHANMSG", "Channel Msg Hilight", 4, NULL },
+	{ "XC_CHANNOTICE", "Channel Notice", 3, NULL },
+	{ "XC_CHANOP", "Channel Operator", 2, NULL },
+	{ "XC_CHANRMEXEMPT", "Channel Remove Exempt", 2, NULL },
+	{ "XC_CHANRMINVITE", "Channel Remove Invite", 2, NULL },
+	{ "XC_CHANRMKEY", "Channel Remove Keyword", 1, NULL },
+	{ "XC_CHANRMLIMIT", "Channel Remove Limit", 1, NULL },
+	{ "XC_CHANSETKEY", "Channel Set Key", 2, NULL },
+	{ "XC_CHANSETLIMIT", "Channel Set Limit", 2, NULL },
+	{ "XC_CHANUNBAN", "Channel UnBan", 2, NULL },
+	{ "XC_CHANVOICE", "Channel Voice", 2, NULL },
+	{ "XC_CONNECTED", "Connected", 0, NULL },
+	{ "XC_CONNECT", "Connecting", 3, NULL },
+	{ "XC_CONNFAIL", "Connection Failed", 1, NULL },
+	{ "XC_CTCPGEN", "CTCP Generic", 2, NULL },
+	{ "XC_CTCPGENC", "CTCP Generic to Channel", 3, NULL },
+	{ "XC_CTCPSEND", "CTCP Send", 2, NULL },
+	{ "XC_CTCPSND", "CTCP Sound", 2, NULL },
+	{ "XC_CTCPSNDC", "CTCP Sound to Channel", 3, NULL },
+	{ "XC_DCCCHATABORT", "DCC CHAT Abort", 1, NULL },
+	{ "XC_DCCCONCHAT", "DCC CHAT Connect", 2, NULL },
+	{ "XC_DCCCHATF", "DCC CHAT Failed", 4, NULL },
+	{ "XC_DCCCHATOFFER", "DCC CHAT Offer", 1, NULL },
+	{ "XC_DCCCHATOFFERING", "DCC CHAT Offering", 1, NULL },
+	{ "XC_DCCCHATREOFFER", "DCC CHAT Reoffer", 1, NULL },
+	{ "XC_DCCCONFAIL", "DCC Conection Failed", 3, NULL },
+	{ "XC_DCCGENERICOFFER", "DCC Generic Offer", 2, NULL },
+	{ "XC_DCCHEAD", "DCC Header", 0, NULL },
+	{ "XC_MALFORMED", "DCC Malformed", 2, NULL },
+	{ "XC_DCCOFFER", "DCC Offer", 3, NULL },
+	{ "XC_DCCIVAL", "DCC Offer Not Valid", 0, NULL },
+	{ "XC_DCCRECVABORT", "DCC RECV Abort", 2, NULL },
+	{ "XC_DCCRECVCOMP", "DCC RECV Complete", 4, NULL },
+	{ "XC_DCCCONRECV", "DCC RECV Connect", 3, NULL },
+	{ "XC_DCCRECVERR", "DCC RECV Failed", 4, NULL },
+	{ "XC_DCCFILEERR", "DCC RECV File Open Error", 2, NULL },
+	{ "XC_DCCRENAME", "DCC Rename", 2, NULL },
+	{ "XC_DCCRESUMEREQUEST", "DCC RESUME Request", 3, NULL },
+	{ "XC_DCCSENDABORT", "DCC SEND Abort", 2, NULL },
+	{ "XC_DCCSENDCOMP", "DCC SEND Complete", 3, NULL },
+	{ "XC_DCCCONSEND", "DCC SEND Connect", 3, NULL },
+	{ "XC_DCCSENDFAIL", "DCC SEND Failed", 3, NULL },
+	{ "XC_DCCSENDOFFER", "DCC SEND Offer", 4, NULL },
+	{ "XC_DCCSTALL", "DCC Stall", 3, NULL },
+	{ "XC_DCCTOUT", "DCC Timeout", 3, NULL },
+	{ "XC_DELNOTIFY", "Delete Notify", 1, NULL },
+	{ "XC_DISCON", "Disconnected", 1, NULL },
+	{ "XC_FOUNDIP", "Found IP", 1, NULL },
+	{ "XC_GENMSG", "Generic Message", 2, NULL },
+	{ "XC_IGNOREADD", "Ignore Add", 1, NULL },
+	{ "XC_IGNORECHANGE", "Ignore Changed", 1, NULL },
+	{ "XC_IGNOREFOOTER", "Ignore Footer", 0, NULL },
+	{ "XC_IGNOREHEADER", "Ignore Header", 0, NULL },
+	{ "XC_IGNOREREMOVE", "Ignore Remove", 1, NULL },
+	{ "XC_IGNOREEMPTY", "Ignorelist Empty", 0, NULL },
+	{ "XC_INVITE", "Invite", 1, NULL },
+	{ "XC_INVITED", "Invited", 3, NULL },
+	{ "XC_JOIN", "Join", 3, NULL },
+	{ "XC_KEYWORD", "Keyword", 1, NULL },
+	{ "XC_KICK", "Kick", 4, NULL },
+	{ "XC_KILL", "Killed", 2, NULL },
+	{ "XC_MSGSEND", "Message Send", 2, NULL },
+	{ "XC_MOTD", "Motd", 1, NULL },
+	{ "XC_MOTDSKIP", "MOTD Skipped", 0, NULL },
+	{ "XC_NICKCLASH", "Nick Clash", 2, NULL },
+	{ "XC_NICKFAIL", "Nick Failed", 0, NULL },
+	{ "XC_NODCC", "No DCC", 0, NULL },
+	{ "XC_NOCHILD", "No Running Process", 0, NULL },
+	{ "XC_NOTICE", "Notice", 2, NULL },
+	{ "XC_NOTICESEND", "Notice Send", 2, NULL },
+	{ "XC_NOTIFYEMPTY", "Notify Empty", 0, NULL },
+	{ "XC_NOTIFYHEAD", "Notify Header", 0, NULL },
+	{ "XC_NOTIFYNUMBER", "Notify Number", 1, NULL },
+	{ "XC_NOTIFYOFFLINE", "Notify Offline", 3, NULL },
+	{ "XC_NOTIFYONLINE", "Notify Online", 3, NULL },
+	{ "XC_OPENDIALOG", "Open Dialog", 0, NULL },
+	{ "XC_PART", "Part", 3, NULL },
+	{ "XC_PARTREASON", "Part with Reason", 4, NULL },
+	{ "XC_PINGREP", "Ping Reply", 2, NULL },
+	{ "XC_PINGTIMEOUT", "Ping Timeout", 1, NULL },
+	{ "XC_PRIVMSG", "Private Message", 3, NULL },
+	{ "XC_DPRIVMSG", "Private Message to Dialog", 3, NULL },
+	{ "XC_ALREADYPROCESS", "Process Already Running", 0, NULL },
+	{ "XC_QUIT", "Quit", 3, NULL },
+	{ "XC_RAWMODES", "Raw Modes", 2, NULL },
+	{ "XC_WALLOPS", "Receive Wallops", 2, NULL },
+	{ "XC_RESOLVINGUSER", "Resolving User", 2, NULL },
+	{ "XC_SERVERCONNECTED", "Server Connected", 0, NULL },
+	{ "XC_SERVERERROR", "Server Error", 1, NULL },
+	{ "XC_SERVERLOOKUP", "Server Lookup", 1, NULL },
+	{ "XC_SERVNOTICE", "Server Notice", 2, NULL },
+	{ "XC_SERVTEXT", "Server Text", 2, NULL },
+	{ "XC_STOPCONNECT", "Stop Connection", 1, NULL },
+	{ "XC_TOPIC", "Topic", 2, NULL },
+	{ "XC_NEWTOPIC", "Topic Change", 3, NULL },
+	{ "XC_TOPICDATE", "Topic Creation", 3, NULL },
+	{ "XC_UKNHOST", "Unknown Host", 0, NULL },
+	{ "XC_USERLIMIT", "User Limit", 1, NULL },
+	{ "XC_USERSONCHAN", "Users On Channel", 2, NULL },
+	{ "XC_WHOIS_AUTH", "WhoIs Authenticated", 3, NULL },
+	{ "XC_WHOIS5", "WhoIs Away Line", 2, NULL },
+	{ "XC_WHOIS2", "WhoIs Channel/Oper Line", 2, NULL },
+	{ "XC_WHOIS6", "WhoIs End", 1, NULL },
+	{ "XC_WHOIS_ID", "WhoIs Identified", 2, NULL },
+	{ "XC_WHOIS4", "WhoIs Idle Line", 2, NULL },
+	{ "XC_WHOIS4T", "WhoIs Idle Line with Signon", 3, NULL },
+	{ "XC_WHOIS1", "WhoIs Name Line", 4, NULL },
+	{ "XC_WHOIS_REALHOST", "WhoIs Real Host", 4, NULL },
+	{ "XC_WHOIS3", "WhoIs Server Line", 2, NULL },
+	{ "XC_WHOIS_SPECIAL", "WhoIs Special", 3, NULL },
+	{ "XC_UJOIN", "You Join", 3, NULL },
+	{ "XC_UKICK", "You Kicked", 4, NULL },
+	{ "XC_UPART", "You Part", 3, NULL },
+	{ "XC_UPARTREASON", "You Part with Reason", 4, NULL },
+	{ "XC_UACTION", "Your Action", 3, NULL },
+	{ "XC_UINVITE", "Your Invitation", 3, NULL },
+	{ "XC_UCHANMSG", "Your Message", 4, NULL },
+	{ "XC_UCHANGENICK", "Your Nick Changing", 2, NULL }
+};
+
diff --git a/plugins/tcl/tclplugin.c b/plugins/tcl/tclplugin.c
new file mode 100644
index 00000000..17dc7556
--- /dev/null
+++ b/plugins/tcl/tclplugin.c
@@ -0,0 +1,2290 @@
+/***************************************************************************
+                           tclplugin.c  -  Tcl plugin for xchat 1.9.x / 2.x.x
+                           -------------------------------------------------s
+    begin                : Sat Nov 19 17:31:20 MST 2002
+    copyright            : Copyright 2002-2010 Daniel P. Stasinski
+    email                : daniel@avenues.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   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.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+static char RCSID[] = "$Id: tclplugin.c,v 1.64 2010/03/10 04:24:16 mooooooo Exp $";
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <limits.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <tcl.h>
+#include <tclDecls.h>
+#include <sys/stat.h>
+
+#ifdef WIN32
+#include <windows.h>
+#define bzero(mem, sz) memset((mem), 0, (sz))
+#define bcopy(src, dest, count) memmove((dest), (src), (count))
+#else
+#include <unistd.h>
+#endif
+
+#include "xchat-plugin.h"
+#include "tclplugin.h"
+#include "printevents.h"
+
+static int nexttimerid = 0;
+static int nexttimerindex = 0;
+static timer timers[MAX_TIMERS];
+
+static char VERSION[16];
+
+static int initialized = 0;
+static int reinit_tried = 0;
+static Tcl_Interp *interp = NULL;
+static xchat_plugin *ph;
+static xchat_hook *raw_line_hook;
+static xchat_hook *Command_TCL_hook;
+static xchat_hook *Command_Source_hook;
+static xchat_hook *Command_Reload_hook;
+static xchat_hook *Command_Load_hook;
+static xchat_hook *Event_Handler_hook;
+static xchat_hook *Null_Command_hook;
+
+static int complete_level = 0;
+static t_complete complete[MAX_COMPLETES + 1];
+static Tcl_HashTable cmdTablePtr;
+static Tcl_HashTable aliasTablePtr;
+
+static int nextprocid = 0x1000;
+#define PROCPREFIX "__xctcl_"
+
+static char unknown[] = {
+"rename unknown iunknown\n"
+"proc ::unknown {args} {\n"
+"  global errorInfo errorCode\n"
+"  if { [string index [lindex $args 0] 0] == \"/\" } {\n"
+"    command \"[string range [join $args \" \"] 1 end]\"\n"
+"  } else {\n"
+"    set code [catch {uplevel iunknown $args} msg]\n"
+"    if {$code == 1} {\n"
+"      set new [split $errorInfo \\n]\n"
+"      set new [join [lrange $new 0 [expr {[llength $new] - 8}]] \\n]\n"
+"      return -code error -errorcode $errorCode -errorinfo $new $msg\n"
+"    } else {\n"
+"      return -code $code $msg\n"
+"    }\n"
+"  }\n"
+"}\n"
+"proc unsupported0 {from to {bytes \"\"}} {\n"
+"  set b [expr {$bytes == \"\" ? \"\" : \"-size [list $bytes]\"}]\n"
+"  eval [list fcopy $from $to] $b\n"
+"}\n"
+};
+
+static char sourcedirs[] = {
+    "set files [lsort [glob -nocomplain -directory [xchatdir] \"*.tcl\"]]\n"
+        "set init [lsearch -glob $files \"*/init.tcl\"]\n"
+        "if { $init > 0 } {\n"
+        "set initfile [lindex $files $init]\n"
+        "set files [lreplace $files $init $init]\n"
+        "set files [linsert $files 0 $initfile]\n" "}\n" "foreach f $files {\n" "if { [catch { source $f } errMsg] } {\n" "puts \"Tcl plugin\\tError sourcing \\\"$f\\\" ($errMsg)\"\n" "} else {\n" "puts \"Tcl plugin\\tSourced \\\"$f\\\"\"\n" "}\n" "}\n"
+};
+
+static char inlinetcl[] = {
+"proc splitsrc { } {\n"
+"uplevel \"scan \\$_src \\\"%\\\\\\[^!\\\\\\]!%\\\\\\[^@\\\\\\]@%s\\\" _nick _ident _host\"\n"
+"}\n"
+
+"proc ::exit { } {\n"
+"puts \"Using 'exit' is bad\"\n"
+"}\n"
+
+"proc ::away { args } { return [eval [join [list getinfo $args away]]] }\n"
+"proc ::channel { args } { return [eval [join [list getinfo $args channel]]] }\n"
+"proc ::tab { args } { return [eval [join [list getinfo $args channel]]] }\n"
+"proc ::charset { args } { return [eval [join [list getinfo $args charset]]] }\n"
+"proc ::host { args } { return [eval [join [list getinfo $args host]]] }\n"
+"proc ::inputbox { args } { return [eval [join [list getinfo $args inputbox]]] }\n"
+"proc ::libdirfs { args } { return [eval [join [list getinfo $args libdirfs]]] }\n"
+"proc ::network { args } { return [eval [join [list getinfo $args network]]] }\n"
+"proc ::nickserv { args } { return [eval [join [list getinfo $args nickserv]]] }\n"
+"proc ::server { args } { return [eval [join [list getinfo $args server]]] }\n"
+"proc ::version { args } { return [eval [join [list getinfo $args version]]] }\n"
+"proc ::win_status { args } { return [eval [join [list getinfo $args win_status]]] }\n"
+"proc ::xchatdir { args } { return [eval [join [list getinfo $args xchatdir]]] }\n"
+"proc ::xchatdirfs { args } { return [eval [join [list getinfo $args xchatdirfs]]] }\n"
+
+"proc ::color { {arg {}} } { return \"\\003$arg\" }\n"
+"proc ::bold { } { return \"\\002\" }\n"
+"proc ::underline { } { return \"\\037\" }\n"
+"proc ::reverse { } { return \"\\026\" }\n"
+"proc ::reset { } { return \"\\017\" }\n"
+
+"proc ::__xctcl_errorInfo { } {\n"
+"      set text [split $::errorInfo \\n]\n"
+"      puts [string trim [join [lrange $text 0 [expr {[llength $text] - 4}]] \\n]]\n"
+"}\n"
+
+"proc ::bgerror { message } {\n"
+"      set text [split $::errorInfo \\n]\n"
+"      puts [string trim [join [lrange $text 0 [expr {[llength $text] - 4}]] \\n]]\n"
+"}\n"
+};
+
+static void NiceErrorInfo ()
+{
+    Tcl_Eval(interp, "::__xctcl_errorInfo");
+}
+
+static void Tcl_MyDStringAppend(Tcl_DString * ds, char *string)
+{
+    Tcl_DStringAppend(ds, string, strlen(string));
+}
+
+static char *InternalProcName(int value)
+{
+    static char result[32];
+    sprintf(result, "%s%08x", PROCPREFIX, value);
+    return result;
+}
+
+static int SourceInternalProc(int id, const char *args, const char *source)
+{
+    Tcl_DString ds;
+    int result;
+    Tcl_DStringInit(&ds);
+
+    Tcl_MyDStringAppend(&ds, "proc ");
+    Tcl_MyDStringAppend(&ds, InternalProcName(id));
+    Tcl_MyDStringAppend(&ds, " { ");
+    Tcl_MyDStringAppend(&ds, args);
+    Tcl_MyDStringAppend(&ds, " } {\n");
+    Tcl_MyDStringAppend(&ds, source);
+    Tcl_MyDStringAppend(&ds, "\n}\n\n");
+
+    result = Tcl_Eval(interp, ds.string);
+
+    Tcl_DStringFree(&ds);
+
+    return result;
+}
+
+static int EvalInternalProc(const char *procname, int ct, ...)
+{
+    Tcl_DString ds;
+    int result;
+    va_list ap;
+    char *buf;
+
+    Tcl_DStringInit(&ds);
+
+    Tcl_MyDStringAppend(&ds, procname);
+
+    if (ct) {
+        va_start(ap, ct);
+        while (ct--) {
+            if ((buf = va_arg(ap, char *)) != NULL)
+                 Tcl_DStringAppendElement(&ds, buf);
+            else
+                Tcl_MyDStringAppend(&ds, " \"\"");
+        }
+        va_end(ap);
+    }
+
+    result = Tcl_Eval(interp, ds.string);
+
+    Tcl_DStringFree(&ds);
+
+    return result;
+}
+
+
+static void DeleteInternalProc(const char *proc)
+{
+    Tcl_DString ds;
+
+    Tcl_DStringInit(&ds);
+    Tcl_MyDStringAppend(&ds, "rename ");
+    Tcl_MyDStringAppend(&ds, proc);
+    Tcl_MyDStringAppend(&ds, " {}");
+    Tcl_Eval(interp, ds.string);
+    Tcl_DStringFree(&ds);
+}
+
+static char *StrDup(const char *string, int *length)
+{
+    char *result;
+
+    if (string == NULL)
+        return NULL;
+
+    *length = strlen(string);
+    result = Tcl_Alloc(*length + 1);
+    strncpy(result, string, *length);
+    result[*length] = 0;
+
+    return result;
+}
+
+static char *myitoa(long value)
+{
+    static char result[32];
+    sprintf(result, "%ld", value);
+    return result;
+}
+
+static xchat_context *atoctx(const char *nptr)
+{
+    int isnum = 1;
+    int x = 0;
+
+    if (!nptr)
+        return NULL;
+
+    while (isnum && nptr[x]) {
+        if (!isdigit(nptr[x++]))
+            isnum--;
+    }
+
+    if (isnum && x)
+        return (xchat_context *) atol(nptr);
+    else
+        return NULL;
+}
+
+static xchat_context *xchat_smart_context(const char *arg1, const char *arg2)
+{
+    const char *server, *s, *n;
+    xchat_context *result = NULL;
+    xchat_context *ctx = NULL;
+    xchat_context *actx = NULL;
+    xchat_list *list;
+
+    if (arg1 == NULL)
+        return xchat_get_context(ph);;
+
+    if (arg1 && arg2) {
+        result = xchat_find_context(ph, arg1, arg2);
+        if (result == NULL)
+            result = xchat_find_context(ph, arg2, arg1);
+        return result;
+    } else {
+
+        actx = atoctx(arg1);
+
+        server = xchat_get_info(ph, "server");
+
+        list = xchat_list_get(ph, "channels");
+
+        if (list != NULL) {
+
+            while (xchat_list_next(ph, list)) {
+
+                ctx = (xchat_context *)xchat_list_str(ph, list, "context");
+
+                if (actx) {
+                    if (ctx == actx) {
+                        result = ctx;
+                        break;
+                    }
+                } else {
+
+                    s = xchat_list_str(ph, list, "server");
+
+                    if (xchat_list_int(ph, list, "type") == 1) {
+                        if (strcasecmp(arg1, s) == 0) {
+                            result = ctx;
+                            break;
+                        }
+                        n = xchat_list_str(ph, list, "network");
+                        if (n) {
+                            if (strcasecmp(arg1, n) == 0) {
+                                result = ctx;
+                                break;
+                            }
+                        }
+                    } else {
+                        if ((strcasecmp(server, s) == 0) && (strcasecmp(arg1, xchat_list_str(ph, list, "channel")) == 0)) {
+                            result = ctx;
+                            break;
+                        }
+                    }
+                }
+            }
+
+            xchat_list_free(ph, list);
+        }
+
+    }
+
+    return result;
+}
+
+static void queue_nexttimer()
+{
+    int x;
+    time_t then;
+
+    nexttimerindex = 0;
+    then = (time_t) 0x7fffffff;
+
+    for (x = 1; x < MAX_TIMERS; x++) {
+        if (timers[x].timerid) {
+            if (timers[x].timestamp < then) {
+                then = timers[x].timestamp;
+                nexttimerindex = x;
+            }
+        }
+    }
+}
+
+static int insert_timer(int seconds, int count, const char *script)
+{
+    int x;
+    int dummy;
+    time_t now;
+    int id;
+
+    if (script == NULL)
+        return (-1);
+
+    id = (nextprocid++ % INT_MAX) + 1;
+
+    now = time(NULL);
+
+    for (x = 1; x < MAX_TIMERS; x++) {
+        if (timers[x].timerid == 0) {
+            if (SourceInternalProc(id, "", script) == TCL_ERROR) {
+                xchat_printf(ph, "\0039TCL plugin\003\tERROR (timer %d) ", timers[x].timerid);
+                NiceErrorInfo ();
+                return (-1);
+            }
+            timers[x].timerid = (nexttimerid++ % INT_MAX) + 1;
+            timers[x].timestamp = now + seconds;
+            timers[x].count = count;
+            timers[x].seconds = seconds;
+            timers[x].procPtr = StrDup(InternalProcName(id), &dummy);
+            queue_nexttimer();
+            return (timers[x].timerid);
+        }
+    }
+
+    return (-1);
+}
+
+static void do_timer()
+{
+    xchat_context *origctx;
+    time_t now;
+    int index;
+
+    if (!nexttimerindex)
+        return;
+
+    now = time(NULL);
+
+    if (now < timers[nexttimerindex].timestamp)
+        return;
+
+    index = nexttimerindex;
+    origctx = xchat_get_context(ph);
+    if (EvalInternalProc(timers[index].procPtr, 0) == TCL_ERROR) {
+        xchat_printf(ph, "\0039TCL plugin\003\tERROR (timer %d) ", timers[index].timerid);
+        NiceErrorInfo ();
+    }
+    xchat_set_context(ph, origctx);
+
+    if (timers[index].count != -1)
+      timers[index].count--;
+
+    if (timers[index].count == 0) {
+      timers[index].timerid = 0;
+      if (timers[index].procPtr != NULL) {
+          DeleteInternalProc(timers[index].procPtr);
+          Tcl_Free(timers[index].procPtr);
+      }
+      timers[index].procPtr = NULL;
+    } else {
+      timers[index].timestamp += timers[index].seconds;
+    }
+
+    queue_nexttimer();
+
+    return;
+
+}
+
+static int Server_raw_line(char *word[], char *word_eol[], void *userdata)
+{
+    char *src, *cmd, *dest, *rest;
+    char *chancmd = NULL;
+    char *procList;
+    Tcl_HashEntry *entry = NULL;
+    xchat_context *origctx;
+    int len;
+    int dummy;
+    char *string = NULL;
+    int ctcp = 0;
+    int count;
+    int list_argc, proc_argc;
+    const char **list_argv, **proc_argv;
+    int private = 0;
+
+    if (word[0][0] == 0)
+        return XCHAT_EAT_NONE;
+
+    if (complete_level == MAX_COMPLETES)
+        return XCHAT_EAT_NONE;
+
+    complete_level++;
+    complete[complete_level].defresult = XCHAT_EAT_NONE;     /* XCHAT_EAT_PLUGIN; */
+    complete[complete_level].result = XCHAT_EAT_NONE;
+    complete[complete_level].word = word;
+	complete[complete_level].word_eol = word_eol;
+
+    if (word[1][0] == ':') {
+        src = word[1];
+        cmd = word[2];
+        dest = word[3];
+        rest = word_eol[4];
+    } else {
+        src = "";
+        cmd = word[1];
+        if (word_eol[2][0] == ':') {
+            dest = "";
+            rest = word_eol[2];
+        } else {
+            dest = word[2];
+            rest = word_eol[3];
+        }
+    }
+
+    if (src[0] == ':')
+        src++;
+    if (dest[0] == ':')
+        dest++;
+    if (rest[0] == ':')
+        rest++;
+
+    if (rest[0] == 0x01) {
+        rest++;
+        if (strcasecmp("PRIVMSG", cmd) == 0) {
+            if (strncasecmp(rest, "ACTION ", 7) == 0) {
+                cmd = "ACTION";
+                rest += 7;
+            } else
+                cmd = "CTCP";
+        } else if (!strcasecmp("NOTICE", cmd))
+            cmd = "CTCR";
+        ctcp = 1;
+    } else if (!strcasecmp("NOTICE", cmd) && (strchr(src, '!') == NULL)) {
+        cmd = "SNOTICE";
+    } else if (rest[0] == '!') {
+        chancmd = word[4] + 1;
+    }
+
+    if (chancmd != NULL) {
+        string = StrDup(chancmd, &dummy);
+        Tcl_UtfToUpper(string);
+        if ((entry = Tcl_FindHashEntry(&cmdTablePtr, string)) == NULL) {
+            Tcl_Free(string);
+        } else {
+            cmd = chancmd;
+            rest = word_eol[5];
+        }
+    }
+
+    if (entry == NULL) {
+        string = StrDup(cmd, &dummy);
+        Tcl_UtfToUpper(string);
+        entry = Tcl_FindHashEntry(&cmdTablePtr, string);
+    }
+
+    if (entry != NULL) {
+
+        procList = Tcl_GetHashValue(entry);
+
+        if (isalpha(dest[0]))
+            private = 1;
+
+        rest = StrDup(rest, &len);
+        if (ctcp) {
+            if (rest != NULL) {
+                if ((len > 1) && (rest[len - 1] == 0x01))
+                    rest[len - 1] = 0;
+            }
+        }
+
+        if (Tcl_SplitList(interp, procList, &list_argc, &list_argv) == TCL_OK) {
+
+            for (count = 0; count < list_argc; count++) {
+
+                if (Tcl_SplitList(interp, list_argv[count], &proc_argc, &proc_argv) != TCL_OK)
+                    continue;
+
+                origctx = xchat_get_context(ph);
+                if (EvalInternalProc(proc_argv[1], 7, src, dest, cmd, rest, word_eol[1], proc_argv[0], myitoa(private)) == TCL_ERROR) {
+                    xchat_printf(ph, "\0039TCL plugin\003\tERROR (on %s %s) ", cmd, proc_argv[0]);
+                    NiceErrorInfo ();
+                }
+                xchat_set_context(ph, origctx);
+
+                Tcl_Free((char *) proc_argv);
+
+                if ((complete[complete_level].result ==  XCHAT_EAT_PLUGIN) || (complete[complete_level].result == XCHAT_EAT_ALL))
+                    break;
+
+            }
+
+            Tcl_Free((char *) list_argv);
+
+        }
+
+        Tcl_Free(rest);
+
+    }
+
+    if (string)
+        Tcl_Free(string);
+
+    return complete[complete_level--].result;
+
+}
+
+static int Print_Hook(char *word[], void *userdata)
+{
+    char *procList;
+    Tcl_HashEntry *entry;
+    xchat_context *origctx;
+    int count;
+    int list_argc, proc_argc;
+    const char **list_argv, **proc_argv;
+    Tcl_DString ds;
+    int x;
+
+    if (complete_level == MAX_COMPLETES)
+        return XCHAT_EAT_NONE;
+
+    complete_level++;
+    complete[complete_level].defresult = XCHAT_EAT_NONE;     /* XCHAT_EAT_PLUGIN; */
+    complete[complete_level].result = XCHAT_EAT_NONE;
+    complete[complete_level].word = word;
+	complete[complete_level].word_eol = word;
+
+    if ((entry = Tcl_FindHashEntry(&cmdTablePtr, xc[(int) userdata].event)) != NULL) {
+
+        procList = Tcl_GetHashValue(entry);
+
+        if (Tcl_SplitList(interp, procList, &list_argc, &list_argv) == TCL_OK) {
+
+            for (count = 0; count < list_argc; count++) {
+
+                if (Tcl_SplitList(interp, list_argv[count], &proc_argc, &proc_argv) != TCL_OK)
+                    continue;
+
+                origctx = xchat_get_context(ph);
+
+                Tcl_DStringInit(&ds);
+
+                if ((int) userdata == CHAT) {
+                    Tcl_DStringAppend(&ds, word[3], strlen(word[3]));
+                    Tcl_DStringAppend(&ds, "!*@", 3);
+                    Tcl_DStringAppend(&ds, word[1], strlen(word[1]));
+                    if (EvalInternalProc(proc_argv[1], 7, ds.string, word[2], xc[(int) userdata].event, word[4], "", proc_argv[0], "0") == TCL_ERROR) {
+                        xchat_printf(ph, "\0039TCL plugin\003\tERROR (on %s %s) ", xc[(int) userdata].event, proc_argv[0]);
+                        NiceErrorInfo ();
+                    }
+                } else {
+                    if (xc[(int) userdata].argc > 0) {
+                        for (x = 0; x <= xc[(int) userdata].argc; x++)
+                            Tcl_DStringAppendElement(&ds, word[x]);
+                    }
+                    if (EvalInternalProc(proc_argv[1], 7, "", "", xc[(int) userdata].event, "", ds.string, proc_argv[0], "0") == TCL_ERROR) {
+                        xchat_printf(ph, "\0039Tcl plugin\003\tERROR (on %s %s) ", xc[(int) userdata].event, proc_argv[0]);
+                        NiceErrorInfo ();
+                    }
+                }
+
+                Tcl_DStringFree(&ds);
+
+                xchat_set_context(ph, origctx);
+
+                Tcl_Free((char *) proc_argv);
+
+                if ((complete[complete_level].result ==  XCHAT_EAT_PLUGIN) || (complete[complete_level].result ==  XCHAT_EAT_ALL))
+                    break;
+
+            }
+
+            Tcl_Free((char *) list_argv);
+
+        }
+    }
+
+    return complete[complete_level--].result;
+}
+
+
+static int tcl_timerexists(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    int x;
+    int timerid;
+
+    BADARGS(2, 2, " schedid");
+
+    if (Tcl_GetInt(irp, argv[1], &timerid) != TCL_OK) {
+        Tcl_AppendResult(irp, "Invalid timer id", NULL);
+        return TCL_ERROR;
+    }
+
+    if (timerid) {
+        for (x = 1; x < MAX_TIMERS; x++) {
+            if (timers[x].timerid == timerid) {
+                Tcl_AppendResult(irp, "1", NULL);
+                return TCL_OK;
+            }
+        }
+    }
+
+    Tcl_AppendResult(irp, "0", NULL);
+
+    return TCL_OK;
+}
+
+static int tcl_killtimer(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    int x;
+    int timerid;
+
+    BADARGS(2, 2, " timerid");
+
+    if (Tcl_GetInt(irp, argv[1], &timerid) != TCL_OK) {
+        Tcl_AppendResult(irp, "Invalid timer id", NULL);
+        return TCL_ERROR;
+    }
+
+    if (timerid) {
+        for (x = 1; x < MAX_TIMERS; x++) {
+            if (timers[x].timerid == timerid) {
+                timers[x].timerid = 0;
+                if (timers[x].procPtr != NULL) {
+                    DeleteInternalProc(timers[x].procPtr);
+                    Tcl_Free(timers[x].procPtr);
+                }
+                timers[x].procPtr = NULL;
+                break;
+            }
+        }
+    }
+
+    queue_nexttimer();
+
+    return TCL_OK;
+}
+
+static int tcl_timers(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    int x;
+    Tcl_DString ds;
+    time_t now;
+
+    BADARGS(1, 1, "");
+
+    now = time(NULL);
+
+    Tcl_DStringInit(&ds);
+
+    for (x = 1; x < MAX_TIMERS; x++) {
+        if (timers[x].timerid) {
+            Tcl_DStringStartSublist(&ds);
+            Tcl_DStringAppendElement(&ds, myitoa((long)timers[x].timerid));
+            Tcl_DStringAppendElement(&ds, myitoa((long)timers[x].timestamp - now));
+            Tcl_DStringAppendElement(&ds, timers[x].procPtr);
+            Tcl_DStringAppendElement(&ds, myitoa((long)timers[x].seconds));
+            Tcl_DStringAppendElement(&ds, myitoa((long)timers[x].count));
+            Tcl_DStringEndSublist(&ds);
+        }
+    }
+
+    Tcl_AppendResult(interp, ds.string, NULL);
+    Tcl_DStringFree(&ds);
+
+    return TCL_OK;
+}
+
+static int tcl_timer(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    int seconds;
+    int timerid;
+    int repeat = 0;
+    int count = 0;
+    int first = 1;
+    char reply[32];
+
+    BADARGS(3, 6, " ?-repeat? ?-count times? seconds {script | procname ?args?}");
+
+    while (argc--) {
+        if (strcasecmp(argv[first], "-repeat") == 0) {
+            repeat++;
+        } else if (strcasecmp(argv[first], "-count") == 0) {
+            if (Tcl_GetInt(irp, argv[++first], &count) != TCL_OK)
+                return TCL_ERROR;
+        } else {
+            break;
+        }
+        first++;
+    }
+
+    if (repeat && !count)
+      count = -1;
+
+    if (!count)
+      count = 1;
+
+    if (Tcl_GetInt(irp, argv[first++], &seconds) != TCL_OK)
+        return TCL_ERROR;
+
+    if ((timerid = insert_timer(seconds, count, argv[first++])) == -1) {
+        Tcl_AppendResult(irp, "0", NULL);
+        return TCL_ERROR;
+    }
+
+    sprintf(reply, "%d", timerid);
+
+    Tcl_AppendResult(irp, reply, NULL);
+
+    queue_nexttimer();
+
+    return TCL_OK;
+}
+
+static int tcl_on(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    int newentry;
+    char *procList;
+    Tcl_HashEntry *entry;
+    char *token;
+    int dummy;
+    Tcl_DString ds;
+    int index;
+    int count;
+    int list_argc, proc_argc;
+    int id;
+    const char **list_argv, **proc_argv;
+
+    BADARGS(4, 4, " token label {script | procname ?args?}");
+
+    id = (nextprocid++ % INT_MAX) + 1;
+
+    if (SourceInternalProc(id, "_src _dest _cmd _rest _raw _label _private", argv[3]) == TCL_ERROR) {
+        xchat_printf(ph, "\0039Tcl plugin\003\tERROR (on %s:%s) ", argv[1], argv[2]);
+        NiceErrorInfo ();
+        return TCL_OK;
+    }
+
+    token = StrDup(argv[1], &dummy);
+    Tcl_UtfToUpper(token);
+
+    Tcl_DStringInit(&ds);
+
+    entry = Tcl_CreateHashEntry(&cmdTablePtr, token, &newentry);
+    if (!newentry) {
+        procList = Tcl_GetHashValue(entry);
+        if (Tcl_SplitList(interp, procList, &list_argc, &list_argv) == TCL_OK) {
+            for (count = 0; count < list_argc; count++) {
+                if (Tcl_SplitList(interp, list_argv[count], &proc_argc, &proc_argv) != TCL_OK)
+                    continue;
+                if (strcmp(proc_argv[0], argv[2])) {
+                    Tcl_DStringStartSublist(&ds);
+                    Tcl_DStringAppendElement(&ds, proc_argv[0]);
+                    Tcl_DStringAppendElement(&ds, proc_argv[1]);
+                    Tcl_DStringEndSublist(&ds);
+                } else {
+                    DeleteInternalProc(proc_argv[1]);
+                }
+                Tcl_Free((char *) proc_argv);
+            }
+            Tcl_Free((char *) list_argv);
+        }
+        Tcl_Free(procList);
+    }
+
+    Tcl_DStringStartSublist(&ds);
+    Tcl_DStringAppendElement(&ds, argv[2]);
+    Tcl_DStringAppendElement(&ds, InternalProcName(id));
+    Tcl_DStringEndSublist(&ds);
+
+    procList = StrDup(ds.string, &dummy);
+
+    Tcl_SetHashValue(entry, procList);
+
+    if ((strncmp(token, "XC_", 3) == 0) || (strcmp(token, "CHAT") == 0)) {
+        for (index = 0; index < XC_SIZE; index++) {
+            if (strcmp(xc[index].event, token) == 0) {
+                if (xc[index].hook == NULL) {
+                    xc[index].hook = xchat_hook_print(ph, xc[index].emit, XCHAT_PRI_NORM, Print_Hook, (void *) index);
+                    break;
+                }
+            }
+        }
+    }
+
+    Tcl_Free(token);
+    Tcl_DStringFree(&ds);
+
+    return TCL_OK;
+}
+
+static int tcl_off(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    char *procList;
+    Tcl_HashEntry *entry;
+    char *token;
+    int dummy;
+    Tcl_DString ds;
+    int index;
+    int count;
+    int list_argc, proc_argc;
+    const char **list_argv, **proc_argv;
+
+    BADARGS(2, 3, " token ?label?");
+
+    token = StrDup(argv[1], &dummy);
+    Tcl_UtfToUpper(token);
+
+    Tcl_DStringInit(&ds);
+
+    if ((entry = Tcl_FindHashEntry(&cmdTablePtr, token)) != NULL) {
+
+        procList = Tcl_GetHashValue(entry);
+
+        if (argc == 3) {
+            if (Tcl_SplitList(interp, procList, &list_argc, &list_argv) == TCL_OK) {
+                for (count = 0; count < list_argc; count++) {
+                    if (Tcl_SplitList(interp, list_argv[count], &proc_argc, &proc_argv) != TCL_OK)
+                        continue;
+                    if (strcmp(proc_argv[0], argv[2])) {
+                        Tcl_DStringStartSublist(&ds);
+                        Tcl_DStringAppendElement(&ds, proc_argv[0]);
+                        Tcl_DStringAppendElement(&ds, proc_argv[1]);
+                        Tcl_DStringEndSublist(&ds);
+                    } else
+                        DeleteInternalProc(proc_argv[1]);
+                    Tcl_Free((char *) proc_argv);
+                }
+                Tcl_Free((char *) list_argv);
+            }
+        }
+
+        Tcl_Free(procList);
+
+        if (ds.length) {
+            procList = StrDup(ds.string, &dummy);
+            Tcl_SetHashValue(entry, procList);
+        } else {
+            Tcl_DeleteHashEntry(entry);
+        }
+
+        if (!ds.length) {
+            if ((strncmp(token, "XC_", 3) == 0) || (strcmp(token, "CHAT") == 0)) {
+                for (index = 0; index < XC_SIZE; index++) {
+                    if (strcmp(xc[index].event, token) == 0) {
+                        if (xc[index].hook != NULL) {
+                            xchat_unhook(ph, xc[index].hook);
+                            xc[index].hook = NULL;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    Tcl_Free(token);
+    Tcl_DStringFree(&ds);
+
+    return TCL_OK;
+}
+
+static int tcl_alias(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    int newentry;
+    alias *aliasPtr;
+    Tcl_HashEntry *entry;
+    char *string;
+    const char *help = NULL;
+    int dummy;
+    int id;
+
+    BADARGS(3, 4, " name ?help? {script | procname ?args?}");
+
+    string = StrDup(argv[1], &dummy);
+    Tcl_UtfToUpper(string);
+
+    if (strlen(argv[argc - 1])) {
+
+        if (argc == 4)
+            help = argv[2];
+
+        id = (nextprocid++ % INT_MAX) + 1;
+
+        if (SourceInternalProc(id, "_cmd _rest", argv[argc - 1]) == TCL_ERROR) {
+            xchat_printf(ph, "\0039Tcl plugin\003\tERROR (alias %s) ", argv[1]);
+            NiceErrorInfo ();
+            return TCL_OK;
+        }
+
+        entry = Tcl_CreateHashEntry(&aliasTablePtr, string, &newentry);
+        if (newentry) {
+            aliasPtr = (alias *) Tcl_Alloc(sizeof(alias));
+            if (string[0] == '@')
+                aliasPtr->hook = NULL;
+            else
+                aliasPtr->hook = xchat_hook_command(ph, string, XCHAT_PRI_NORM, Command_Alias, help, 0);
+        } else {
+            aliasPtr = Tcl_GetHashValue(entry);
+            DeleteInternalProc(aliasPtr->procPtr);
+            Tcl_Free(aliasPtr->procPtr);
+        }
+
+        aliasPtr->procPtr = StrDup(InternalProcName(id), &dummy);
+
+        Tcl_SetHashValue(entry, aliasPtr);
+
+    } else {
+
+        if ((entry = Tcl_FindHashEntry(&aliasTablePtr, string)) != NULL) {
+            aliasPtr = Tcl_GetHashValue(entry);
+            DeleteInternalProc(aliasPtr->procPtr);
+            Tcl_Free(aliasPtr->procPtr);
+            if (aliasPtr->hook)
+                xchat_unhook(ph, aliasPtr->hook);
+            Tcl_Free((char *) aliasPtr);
+            Tcl_DeleteHashEntry(entry);
+        }
+    }
+
+    Tcl_Free(string);
+
+    return TCL_OK;
+}
+
+static int tcl_complete(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    BADARGS(1, 2, " ?EAT_NONE|EAT_XCHAT|EAT_PLUGIN|EAT_ALL?");
+
+    if (argc == 2) {
+        if (Tcl_GetInt(irp, argv[1], &complete[complete_level].result) != TCL_OK) {
+            if (strcasecmp("EAT_NONE", argv[1]) == 0)
+                complete[complete_level].result = XCHAT_EAT_NONE;
+            else if (strcasecmp("EAT_XCHAT", argv[1]) == 0)
+                complete[complete_level].result = XCHAT_EAT_XCHAT;
+            else if (strcasecmp("EAT_PLUGIN", argv[1]) == 0)
+                complete[complete_level].result = XCHAT_EAT_PLUGIN;
+            else if (strcasecmp("EAT_ALL", argv[1]) == 0)
+                complete[complete_level].result = XCHAT_EAT_ALL;
+            else
+                BADARGS(1, 2, " ?EAT_NONE|EAT_XCHAT|EAT_PLUGIN|EAT_ALL?");
+        }
+    } else
+        complete[complete_level].result = complete[complete_level].defresult;
+
+    return TCL_RETURN;
+}
+
+static int tcl_command(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    xchat_context *origctx;
+    xchat_context *ctx = NULL;
+    const char *string = NULL;
+
+    BADARGS(2, 4, " ?server|network|context? ?#channel|nick? text");
+
+    origctx = xchat_get_context(ph);
+
+    switch (argc) {
+    case 2:
+        ctx = origctx;
+        break;
+    case 3:
+        ctx = xchat_smart_context(argv[1], NULL);
+        break;
+    case 4:
+        ctx = xchat_smart_context(argv[1], argv[2]);
+        break;
+    default:;
+    }
+
+    CHECKCTX(ctx);
+
+    string = argv[argc - 1];
+
+    if (string[0] == '/')
+        string++;
+
+    xchat_set_context(ph, ctx);
+    xchat_command(ph, string);
+    xchat_set_context(ph, origctx);
+
+    return TCL_OK;
+}
+
+static int tcl_raw(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    xchat_context *origctx;
+    xchat_context *ctx = NULL;
+    const char *string = NULL;
+
+    BADARGS(2, 4, " ?server|network|context? ?#channel|nick? text");
+
+    origctx = xchat_get_context(ph);
+
+    switch (argc) {
+    case 2:
+        ctx = origctx;
+        break;
+    case 3:
+        ctx = xchat_smart_context(argv[1], NULL);
+        break;
+    case 4:
+        ctx = xchat_smart_context(argv[1], argv[2]);
+        break;
+    default:;
+    }
+
+    CHECKCTX(ctx);
+
+    string = argv[argc - 1];
+
+    xchat_set_context(ph, ctx);
+    xchat_commandf(ph, "RAW %s", string);
+    xchat_set_context(ph, origctx);
+
+    return TCL_OK;
+}
+
+
+static int tcl_prefs(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    int i;
+    const char *str;
+
+    BADARGS(2, 2, " name");
+
+    switch (xchat_get_prefs (ph, argv[1], &str, &i)) {
+        case 1:
+            Tcl_AppendResult(irp, str, NULL);
+            break;
+        case 2:
+        case 3:
+            Tcl_AppendResult(irp, myitoa(i), NULL);
+            break;
+        default:
+            Tcl_AppendResult(irp, NULL);
+    }
+
+    return TCL_OK;
+}
+
+static int tcl_info(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[], char *id)
+{
+    char *result;
+    int max_argc;
+    xchat_context *origctx, *ctx;
+
+    if (id == NULL) {
+        BADARGS(2, 3, " ?server|network|context? id");
+        max_argc = 3;
+    } else {
+        BADARGS(1, 2, " ?server|network|context?");
+        max_argc = 2;
+    }
+
+    origctx = xchat_get_context(ph);
+
+    if (argc == max_argc) {
+        ctx = xchat_smart_context(argv[1], NULL);
+        CHECKCTX(ctx);
+        xchat_set_context(ph, ctx);
+    }
+
+    if (id == NULL)
+      id = argv[argc-1];
+
+    if ((result = xchat_get_info(ph, id)) == NULL)
+        result = "";
+
+    Tcl_AppendResult(irp, result, NULL);
+
+    xchat_set_context(ph, origctx);
+
+    return TCL_OK;
+}
+
+static int tcl_me(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    return tcl_info(cd, irp, argc, argv, "nick");
+}
+
+static int tcl_getinfo(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    return tcl_info(cd, irp, argc, argv, NULL);
+}
+
+static int tcl_getlist(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    xchat_list *list = NULL;
+    const char *name = NULL;
+    const char * const *fields;
+    const char *field;
+    const char *sattr;
+    int iattr;
+    int i;
+    time_t t;
+    Tcl_DString ds;
+    xchat_context *origctx;
+    xchat_context *ctx = NULL;
+
+    origctx = xchat_get_context(ph);
+
+    BADARGS(1, 2, " list");
+
+    Tcl_DStringInit(&ds);
+
+    fields = xchat_list_fields(ph, "lists");
+
+    if (argc == 1) {
+        for (i = 0; fields[i] != NULL; i++) {
+            Tcl_DStringAppendElement(&ds, fields[i]);
+        }
+        goto done;
+    }
+
+    for (i = 0; fields[i] != NULL; i++) {
+        if (strcmp(fields[i], argv[1]) == 0) {
+            name = fields[i];
+            break;
+        }
+    }
+
+    if (name == NULL)
+        goto done;
+
+    list = xchat_list_get(ph, name);
+    if (list == NULL)
+        goto done;
+
+    fields = xchat_list_fields(ph, name);
+
+    Tcl_DStringStartSublist(&ds);
+    for (i = 0; fields[i] != NULL; i++) {
+        field = fields[i] + 1;
+        Tcl_DStringAppendElement(&ds, field);
+    }
+    Tcl_DStringEndSublist(&ds);
+
+    while (xchat_list_next(ph, list)) {
+
+        Tcl_DStringStartSublist(&ds);
+
+        for (i = 0; fields[i] != NULL; i++) {
+
+            field = fields[i] + 1;
+
+            switch (fields[i][0]) {
+            case 's':
+                sattr = xchat_list_str(ph, list, (char *) field);
+                Tcl_DStringAppendElement(&ds, sattr);
+                break;
+            case 'i':
+                iattr = xchat_list_int(ph, list, (char *) field);
+                Tcl_DStringAppendElement(&ds, myitoa((long)iattr));
+                break;
+            case 't':
+                t = xchat_list_time(ph, list, (char *) field);
+                Tcl_DStringAppendElement(&ds, myitoa((long)t));
+                break;
+            case 'p':
+                sattr = xchat_list_str(ph, list, (char *) field);
+                if (strcmp(field, "context") == 0) {
+                    ctx = (xchat_context *) sattr;
+                    Tcl_DStringAppendElement(&ds, myitoa((long)ctx));
+                } else
+                    Tcl_DStringAppendElement(&ds, "*");
+                break;
+            default:
+                Tcl_DStringAppendElement(&ds, "*");
+                break;
+            }
+        }
+
+        Tcl_DStringEndSublist(&ds);
+
+    }
+
+    xchat_list_free(ph, list);
+
+  done:
+
+    xchat_set_context(ph, origctx);
+
+    Tcl_AppendResult(irp, ds.string, NULL);
+
+    Tcl_DStringFree(&ds);
+
+    return TCL_OK;
+}
+
+/*
+ * tcl_xchat_puts - stub for tcl puts command
+ * This is modified from the original internal "puts" command.  It redirects
+ * stdout to the current context, while still allowing all normal puts features
+ */
+
+static int tcl_xchat_puts(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    Tcl_Channel chan;
+    const char *string;
+    int newline;
+    const char *channelId = NULL;
+    int result;
+    int mode;
+    int trap_stdout = 0;
+
+    switch (argc) {
+
+    case 2:
+        string = argv[1];
+        newline = 1;
+        trap_stdout = 1;
+        break;
+
+    case 3:
+        if (strcmp(argv[1], "-nonewline") == 0) {
+            newline = 0;
+            trap_stdout = 1;
+        } else {
+            newline = 1;
+            channelId = argv[1];
+        }
+        string = argv[2];
+        break;
+
+    case 4:
+        if (strcmp(argv[1], "-nonewline") == 0) {
+            channelId = argv[2];
+            string = argv[3];
+        } else {
+            if (strcmp(argv[3], "nonewline") != 0) {
+                Tcl_AppendResult(interp, "bad argument \"", argv[3], "\": should be \"nonewline\"", (char *) NULL);
+                return TCL_ERROR;
+            }
+            channelId = argv[1];
+            string = argv[2];
+        }
+        newline = 0;
+        break;
+
+    default:
+        Tcl_AppendResult(interp, argv, "?-nonewline? ?channelId? string", NULL);
+        return TCL_ERROR;
+    }
+
+    if (!trap_stdout && (strcmp(channelId, "stdout") == 0))
+        trap_stdout = 1;
+
+    if (trap_stdout) {
+        if (newline)
+            xchat_printf(ph, "%s\n", string);
+        else
+            xchat_print(ph, string);
+        return TCL_OK;
+    }
+
+    chan = Tcl_GetChannel(interp, channelId, &mode);
+    if (chan == (Tcl_Channel) NULL) {
+        return TCL_ERROR;
+    }
+    if ((mode & TCL_WRITABLE) == 0) {
+        Tcl_AppendResult(interp, "channel \"", channelId, "\" wasn't opened for writing", (char *) NULL);
+        return TCL_ERROR;
+    }
+
+    result = Tcl_Write(chan, string, strlen(string));
+    if (result < 0) {
+        goto error;
+    }
+    if (newline != 0) {
+        result = Tcl_WriteChars(chan, "\n", 1);
+        if (result < 0) {
+            goto error;
+        }
+    }
+    return TCL_OK;
+
+  error:
+    Tcl_AppendResult(interp, "error writing \"", channelId, "\": ", Tcl_PosixError(interp), (char *) NULL);
+
+    return TCL_ERROR;
+}
+
+static int tcl_print(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    xchat_context *origctx;
+    xchat_context *ctx = NULL;
+    const char *string = NULL;
+
+    BADARGS(2, 4, " ?server|network|context? ?#channel|nick? text");
+
+    origctx = xchat_get_context(ph);
+
+    switch (argc) {
+    case 2:
+        ctx = origctx;
+        break;
+    case 3:
+        ctx = xchat_smart_context(argv[1], NULL);
+        break;
+    case 4:
+        ctx = xchat_smart_context(argv[1], argv[2]);
+        break;
+    default:;
+    }
+
+    CHECKCTX(ctx);
+
+    string = argv[argc - 1];
+
+    xchat_set_context(ph, ctx);
+    xchat_print(ph, string);
+    xchat_set_context(ph, origctx);
+
+    return TCL_OK;
+}
+
+static int tcl_setcontext(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    xchat_context *ctx = NULL;
+
+    BADARGS(2, 2, " context");
+
+    ctx = xchat_smart_context(argv[1], NULL);
+
+    CHECKCTX(ctx);
+
+    xchat_set_context(ph, ctx);
+
+    return TCL_OK;
+}
+
+static int tcl_findcontext(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    xchat_context *ctx = NULL;
+
+    BADARGS(1, 3, " ?server|network|context? ?channel?");
+
+    switch (argc) {
+    case 1:
+        ctx = xchat_find_context(ph, NULL, NULL);
+        break;
+    case 2:
+        ctx = xchat_smart_context(argv[1], NULL);
+        break;
+    case 3:
+        ctx = xchat_smart_context(argv[1], argv[2]);
+        break;
+    default:;
+    }
+
+    CHECKCTX(ctx);
+
+    Tcl_AppendResult(irp, myitoa((long)ctx), NULL);
+
+    return TCL_OK;
+}
+
+static int tcl_getcontext(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    xchat_context *ctx = NULL;
+
+    BADARGS(1, 1, "");
+
+    ctx = xchat_get_context(ph);
+
+    Tcl_AppendResult(irp, myitoa((long)ctx), NULL);
+
+    return TCL_OK;
+}
+
+static int tcl_channels(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    const char *server, *channel;
+    xchat_list *list;
+    Tcl_DString ds;
+    xchat_context *origctx;
+    xchat_context *ctx;
+
+    origctx = xchat_get_context(ph);
+
+    BADARGS(1, 2, " ?server|network|context?");
+
+    if (argc == 2) {
+        ctx = xchat_smart_context(argv[1], NULL);
+        CHECKCTX(ctx);
+        xchat_set_context(ph, ctx);
+    }
+
+    server = (char *) xchat_get_info(ph, "server");
+
+    Tcl_DStringInit(&ds);
+
+    list = xchat_list_get(ph, "channels");
+
+    if (list != NULL) {
+        while (xchat_list_next(ph, list)) {
+            if (xchat_list_int(ph, list, "type") != 2)
+                continue;
+            if (strcasecmp(server, xchat_list_str(ph, list, "server")) != 0)
+                continue;
+            channel = xchat_list_str(ph, list, "channel");
+            Tcl_DStringAppendElement(&ds, channel);
+        }
+        xchat_list_free(ph, list);
+    }
+
+    Tcl_AppendResult(irp, ds.string, NULL);
+
+    Tcl_DStringFree(&ds);
+
+    xchat_set_context(ph, origctx);
+
+    return TCL_OK;
+}
+
+static int tcl_servers(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    const char *server;
+    xchat_list *list;
+    Tcl_DString ds;
+
+    BADARGS(1, 1, "");
+
+    Tcl_DStringInit(&ds);
+
+    list = xchat_list_get(ph, "channels");
+
+    if (list != NULL) {
+        while (xchat_list_next(ph, list)) {
+            if (xchat_list_int(ph, list, "type") == 1) {
+                server = xchat_list_str(ph, list, "server");
+                Tcl_DStringAppendElement(&ds, server);
+            }
+        }
+        xchat_list_free(ph, list);
+    }
+
+    Tcl_AppendResult(irp, ds.string, NULL);
+
+    Tcl_DStringFree(&ds);
+
+    return TCL_OK;
+}
+
+static int tcl_queries(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    const char *server, *channel;
+    xchat_list *list;
+    Tcl_DString ds;
+    xchat_context *origctx;
+    xchat_context *ctx;
+
+    origctx = xchat_get_context(ph);
+
+    BADARGS(1, 2, " ?server|network|context?");
+
+    if (argc == 2) {
+        ctx = xchat_smart_context(argv[1], NULL);
+        CHECKCTX(ctx);
+        xchat_set_context(ph, ctx);
+    }
+
+    server = (char *) xchat_get_info(ph, "server");
+
+    Tcl_DStringInit(&ds);
+
+    list = xchat_list_get(ph, "channels");
+
+    if (list != NULL) {
+        while (xchat_list_next(ph, list)) {
+            if (xchat_list_int(ph, list, "type") != 3)
+                continue;
+            if (strcasecmp(server, xchat_list_str(ph, list, "server")) != 0)
+                continue;
+            channel = xchat_list_str(ph, list, "channel");
+            Tcl_DStringAppendElement(&ds, channel);
+        }
+        xchat_list_free(ph, list);
+    }
+
+    Tcl_AppendResult(irp, ds.string, NULL);
+
+    Tcl_DStringFree(&ds);
+
+    xchat_set_context(ph, origctx);
+
+    return TCL_OK;
+}
+
+static int tcl_users(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    xchat_context *origctx, *ctx = NULL;
+    xchat_list *list;
+    Tcl_DString ds;
+
+    BADARGS(1, 3, " ?server|network|context? ?channel?");
+
+    origctx = xchat_get_context(ph);
+
+    switch (argc) {
+    case 1:
+        ctx = origctx;
+        break;
+    case 2:
+        ctx = xchat_smart_context(argv[1], NULL);
+        break;
+    case 3:
+        ctx = xchat_smart_context(argv[1], argv[2]);
+        break;
+    default:;
+    }
+
+    CHECKCTX(ctx);
+
+    xchat_set_context(ph, ctx);
+
+    Tcl_DStringInit(&ds);
+
+    list = xchat_list_get(ph, "users");
+
+    if (list != NULL) {
+
+        Tcl_DStringStartSublist(&ds);
+        Tcl_DStringAppendElement(&ds, "nick");
+        Tcl_DStringAppendElement(&ds, "host");
+        Tcl_DStringAppendElement(&ds, "prefix");
+        Tcl_DStringAppendElement(&ds, "away");
+        Tcl_DStringAppendElement(&ds, "lasttalk");
+        Tcl_DStringAppendElement(&ds, "selected");
+        Tcl_DStringEndSublist(&ds);
+
+        while (xchat_list_next(ph, list)) {
+            Tcl_DStringStartSublist(&ds);
+            Tcl_DStringAppendElement(&ds, (const char *) xchat_list_str(ph, list, "nick"));
+            Tcl_DStringAppendElement(&ds, (const char *) xchat_list_str(ph, list, "host"));
+            Tcl_DStringAppendElement(&ds, (const char *) xchat_list_str(ph, list, "prefix"));
+            Tcl_DStringAppendElement(&ds, myitoa((long)xchat_list_int(ph, list, "away")));
+            Tcl_DStringAppendElement(&ds, myitoa((long)xchat_list_time(ph, list, "lasttalk")));
+            Tcl_DStringAppendElement(&ds, myitoa((long)xchat_list_int(ph, list, "selected")));
+            Tcl_DStringEndSublist(&ds);
+        }
+
+        xchat_list_free(ph, list);
+    }
+
+    Tcl_AppendResult(irp, ds.string, NULL);
+
+    Tcl_DStringFree(&ds);
+
+    xchat_set_context(ph, origctx);
+
+    return TCL_OK;
+}
+
+static int tcl_notifylist(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    xchat_list *list;
+    Tcl_DString ds;
+
+    BADARGS(1, 1, "");
+
+    Tcl_DStringInit(&ds);
+
+    list = xchat_list_get(ph, "notify");
+
+    if (list != NULL) {
+
+        Tcl_DStringStartSublist(&ds);
+        Tcl_DStringAppendElement(&ds, "nick");
+        Tcl_DStringAppendElement(&ds, "flags");
+        Tcl_DStringAppendElement(&ds, "on");
+        Tcl_DStringAppendElement(&ds, "off");
+        Tcl_DStringAppendElement(&ds, "seen");
+        Tcl_DStringAppendElement(&ds, "networks");
+        Tcl_DStringEndSublist(&ds);
+
+        while (xchat_list_next(ph, list)) {
+            Tcl_DStringStartSublist(&ds);
+            Tcl_DStringAppendElement(&ds, (const char *) xchat_list_str(ph, list, "nick"));
+            Tcl_DStringAppendElement(&ds, (const char *) xchat_list_str(ph, list, "flags"));
+            Tcl_DStringAppendElement(&ds, myitoa((long)xchat_list_time(ph, list, "on")));
+            Tcl_DStringAppendElement(&ds, myitoa((long)xchat_list_time(ph, list, "off")));
+            Tcl_DStringAppendElement(&ds, myitoa((long)xchat_list_time(ph, list, "seen")));
+            Tcl_DStringAppendElement(&ds, (const char *) xchat_list_str(ph, list, "networks"));
+            Tcl_DStringEndSublist(&ds);
+        }
+
+        xchat_list_free(ph, list);
+
+    }
+
+    Tcl_AppendResult(irp, ds.string, NULL);
+
+    Tcl_DStringFree(&ds);
+
+    return TCL_OK;
+}
+
+static int tcl_chats(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    xchat_list *list;
+    Tcl_DString ds;
+
+    BADARGS(1, 1, "");
+
+    Tcl_DStringInit(&ds);
+
+    list = xchat_list_get(ph, "dcc");
+
+    if (list != NULL) {
+        while (xchat_list_next(ph, list)) {
+            switch (xchat_list_int(ph, list, "type")) {
+            case 2:
+            case 3:
+                if (xchat_list_int(ph, list, "status") == 1)
+                    Tcl_DStringAppendElement(&ds, (const char *) xchat_list_str(ph, list, "nick"));
+                break;
+            }
+        }
+        xchat_list_free(ph, list);
+    }
+
+    Tcl_AppendResult(irp, ds.string, NULL);
+
+    Tcl_DStringFree(&ds);
+
+    return TCL_OK;
+}
+
+static int tcl_ignores(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    xchat_list *list;
+    Tcl_DString ds;
+    int flags;
+
+    BADARGS(1, 1, "");
+
+    Tcl_DStringInit(&ds);
+
+    list = xchat_list_get(ph, "ignore");
+
+    if (list != NULL) {
+
+        while (xchat_list_next(ph, list)) {
+            Tcl_DStringStartSublist(&ds);
+            Tcl_DStringAppendElement(&ds, (const char *) xchat_list_str(ph, list, "mask"));
+            Tcl_DStringStartSublist(&ds);
+            flags = xchat_list_int(ph, list, "flags");
+            if (flags & 1)
+                Tcl_DStringAppendElement(&ds, "PRIVMSG");
+            if (flags & 2)
+                Tcl_DStringAppendElement(&ds, "NOTICE");
+            if (flags & 4)
+                Tcl_DStringAppendElement(&ds, "CHANNEL");
+            if (flags & 8)
+                Tcl_DStringAppendElement(&ds, "CTCP");
+            if (flags & 16)
+                Tcl_DStringAppendElement(&ds, "INVITE");
+            if (flags & 32)
+                Tcl_DStringAppendElement(&ds, "UNIGNORE");
+            if (flags & 64)
+                Tcl_DStringAppendElement(&ds, "NOSAVE");
+            Tcl_DStringEndSublist(&ds);
+            Tcl_DStringEndSublist(&ds);
+        }
+        xchat_list_free(ph, list);
+    }
+
+    Tcl_AppendResult(irp, ds.string, NULL);
+
+    Tcl_DStringFree(&ds);
+
+    return TCL_OK;
+}
+
+static int tcl_dcclist(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    xchat_list *list;
+    Tcl_DString ds;
+    int dcctype;
+
+    BADARGS(1, 1, "");
+
+    Tcl_DStringInit(&ds);
+
+    list = xchat_list_get(ph, "dcc");
+
+    if (list != NULL) {
+
+        while (xchat_list_next(ph, list)) {
+            Tcl_DStringStartSublist(&ds);
+            dcctype = xchat_list_int(ph, list, "type");
+            switch (dcctype) {
+            case 0:
+                Tcl_DStringAppendElement(&ds, "filesend");
+                break;
+            case 1:
+                Tcl_DStringAppendElement(&ds, "filerecv");
+                break;
+            case 2:
+                Tcl_DStringAppendElement(&ds, "chatrecv");
+                break;
+            case 3:
+                Tcl_DStringAppendElement(&ds, "chatsend");
+                break;
+            }
+            switch (xchat_list_int(ph, list, "status")) {
+            case 0:
+                Tcl_DStringAppendElement(&ds, "queued");
+                break;
+            case 1:
+                Tcl_DStringAppendElement(&ds, "active");
+                break;
+            case 2:
+                Tcl_DStringAppendElement(&ds, "failed");
+                break;
+            case 3:
+                Tcl_DStringAppendElement(&ds, "done");
+                break;
+            case 4:
+                Tcl_DStringAppendElement(&ds, "connecting");
+                break;
+            case 5:
+                Tcl_DStringAppendElement(&ds, "aborted");
+                break;
+            }
+
+            Tcl_DStringAppendElement(&ds, (const char *) xchat_list_str(ph, list, "nick"));
+
+            switch (dcctype) {
+            case 0:
+                Tcl_DStringAppendElement(&ds, (const char *) xchat_list_str(ph, list, "file"));
+                break;
+            case 1:
+                Tcl_DStringAppendElement(&ds, (const char *) xchat_list_str(ph, list, "destfile"));
+                break;
+            }
+
+            Tcl_DStringAppendElement(&ds, myitoa((long)xchat_list_int(ph, list, "size")));
+            Tcl_DStringAppendElement(&ds, myitoa((long)xchat_list_int(ph, list, "resume")));
+            Tcl_DStringAppendElement(&ds, myitoa((long)xchat_list_int(ph, list, "pos")));
+            Tcl_DStringAppendElement(&ds, myitoa((long)xchat_list_int(ph, list, "cps")));
+            Tcl_DStringAppendElement(&ds, myitoa((long)xchat_list_int(ph, list, "address32")));
+            Tcl_DStringAppendElement(&ds, myitoa((long)xchat_list_int(ph, list, "port")));
+            Tcl_DStringEndSublist(&ds);
+        }
+        xchat_list_free(ph, list);
+    }
+
+    Tcl_AppendResult(irp, ds.string, NULL);
+
+    Tcl_DStringFree(&ds);
+
+    return TCL_OK;
+}
+
+
+static int tcl_strip(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    char *new_text;
+    int flags = 1 | 2;
+
+    BADARGS(2, 3, " text ?flags?");
+
+    if (argc == 3) {
+        if (Tcl_GetInt(irp, argv[2], &flags) != TCL_OK)
+            return TCL_ERROR;
+    }
+
+    new_text = xchat_strip(ph, argv[1], -1, flags);
+
+    if(new_text) {
+        Tcl_AppendResult(irp, new_text, NULL);
+        xchat_free(ph, new_text);
+    }
+
+    return TCL_OK;
+}
+
+static int tcl_topic(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    xchat_context *origctx, *ctx = NULL;
+    BADARGS(1, 3, " ?server|network|context? ?channel?");
+
+    origctx = xchat_get_context(ph);
+
+    switch (argc) {
+    case 1:
+        ctx = origctx;
+        break;
+    case 2:
+        ctx = xchat_smart_context(argv[1], NULL);
+        break;
+    case 3:
+        ctx = xchat_smart_context(argv[1], argv[2]);
+        break;
+    default:;
+    }
+
+    CHECKCTX(ctx);
+
+    xchat_set_context(ph, ctx);
+    Tcl_AppendResult(irp, xchat_get_info(ph, "topic"), NULL);
+    xchat_set_context(ph, origctx);
+
+    return TCL_OK;
+}
+
+static int tcl_xchat_nickcmp(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    BADARGS(3, 3, " string1 string2");
+    Tcl_AppendResult(irp, myitoa((long)xchat_nickcmp(ph, argv[1], argv[2])), NULL);
+    return TCL_OK;
+}
+
+static int tcl_word(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    int index;
+
+    BADARGS(2, 2, " index");
+
+    if (Tcl_GetInt(irp, argv[1], &index) != TCL_OK)
+        return TCL_ERROR;
+
+    if (!index || (index > 31))
+        Tcl_AppendResult(interp, "", NULL);
+    else
+        Tcl_AppendResult(interp, complete[complete_level].word[index], NULL);
+
+    return TCL_OK;
+}
+
+static int tcl_word_eol(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
+{
+    int index;
+
+    BADARGS(2, 2, " index");
+
+    if (Tcl_GetInt(irp, argv[1], &index) != TCL_OK)
+        return TCL_ERROR;
+
+    if (!index || (index > 31))
+        Tcl_AppendResult(interp, "", NULL);
+    else
+        Tcl_AppendResult(interp, complete[complete_level].word_eol[index], NULL);
+
+    return TCL_OK;
+}
+
+static int Command_Alias(char *word[], char *word_eol[], void *userdata)
+{
+    alias *aliasPtr;
+    Tcl_HashEntry *entry;
+    xchat_context *origctx;
+    int dummy;
+    char *string;
+
+    if (complete_level == MAX_COMPLETES)
+        return XCHAT_EAT_NONE;
+
+    complete_level++;
+    complete[complete_level].defresult = XCHAT_EAT_ALL;
+    complete[complete_level].result = XCHAT_EAT_NONE;
+    complete[complete_level].word = word;
+	complete[complete_level].word_eol = word_eol;
+
+    string = StrDup(word[1], &dummy);
+
+    Tcl_UtfToUpper(string);
+
+    if ((entry = Tcl_FindHashEntry(&aliasTablePtr, string)) != NULL) {
+        aliasPtr = Tcl_GetHashValue(entry);
+        origctx = xchat_get_context(ph);
+        if (EvalInternalProc(aliasPtr->procPtr, 2, string, word_eol[2]) == TCL_ERROR) {
+            xchat_printf(ph, "\0039Tcl plugin\003\tERROR (alias %s) ", string);
+            NiceErrorInfo ();
+        }
+        xchat_set_context(ph, origctx);
+    }
+
+    Tcl_Free(string);
+
+    return complete[complete_level--].result;
+}
+
+static int Null_Command_Alias(char *word[], char *word_eol[], void *userdata)
+{
+    alias *aliasPtr;
+    Tcl_HashEntry *entry;
+    xchat_context *origctx;
+    int dummy;
+    const char *channel;
+    char *string;
+    Tcl_DString ds;
+    static int recurse = 0;
+
+    if (recurse)
+        return XCHAT_EAT_NONE;
+
+    if (complete_level == MAX_COMPLETES)
+        return XCHAT_EAT_NONE;
+
+    complete_level++;
+    complete[complete_level].defresult = XCHAT_EAT_ALL;
+    complete[complete_level].result = XCHAT_EAT_NONE;
+    complete[complete_level].word = word;
+	complete[complete_level].word_eol = word_eol;
+
+    recurse++;
+
+    channel = xchat_get_info(ph, "channel");
+    Tcl_DStringInit(&ds);
+    Tcl_DStringAppend(&ds, "@", 1);
+    Tcl_DStringAppend(&ds, channel, strlen(channel));
+    string = StrDup(ds.string, &dummy);
+    Tcl_DStringFree(&ds);
+
+    Tcl_UtfToUpper(string);
+
+    if ((entry = Tcl_FindHashEntry(&aliasTablePtr, string)) != NULL) {
+        aliasPtr = Tcl_GetHashValue(entry);
+        origctx = xchat_get_context(ph);
+        if (EvalInternalProc(aliasPtr->procPtr, 2, string, word_eol[1]) == TCL_ERROR) {
+            xchat_printf(ph, "\0039Tcl plugin\003\tERROR (alias %s) ", string);
+            NiceErrorInfo ();
+        }
+        xchat_set_context(ph, origctx);
+    }
+
+    Tcl_Free(string);
+
+    recurse--;
+
+    return complete[complete_level--].result;
+}
+
+static int Command_TCL(char *word[], char *word_eol[], void *userdata)
+{
+    const char *errorInfo;
+
+    complete_level++;
+    complete[complete_level].word = word;
+    complete[complete_level].word_eol = word_eol;
+
+    if (Tcl_Eval(interp, word_eol[2]) == TCL_ERROR) {
+        errorInfo = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
+        xchat_printf(ph, "\0039Tcl plugin\003\tERROR: %s ", errorInfo);
+    } else
+        xchat_printf(ph, "\0039Tcl plugin\003\tRESULT: %s ", Tcl_GetStringResult(interp));
+
+    complete_level--;
+
+    return XCHAT_EAT_ALL;
+}
+
+static int Command_Source(char *word[], char *word_eol[], void *userdata)
+{
+    const char *xchatdir;
+    Tcl_DString ds;
+    struct stat dummy;
+    int len;
+    const char *errorInfo;
+
+    if (!strlen(word_eol[2]))
+        return XCHAT_EAT_NONE;
+
+    complete_level++;
+    complete[complete_level].word = word;
+    complete[complete_level].word_eol = word_eol;
+
+    len = strlen(word[2]);
+
+    if (len > 4 && strcasecmp(".tcl", word[2] + len - 4) == 0) {
+
+        xchatdir = xchat_get_info(ph, "xchatdir");
+
+        Tcl_DStringInit(&ds);
+
+        if (stat(word_eol[2], &dummy) == 0) {
+            Tcl_DStringAppend(&ds, word_eol[2], strlen(word_eol[2]));
+        } else {
+            if (!strchr(word_eol[2], '/')) {
+                Tcl_DStringAppend(&ds, xchatdir, strlen(xchatdir));
+                Tcl_DStringAppend(&ds, "/", 1);
+                Tcl_DStringAppend(&ds, word_eol[2], strlen(word_eol[2]));
+            }
+        }
+
+        if (Tcl_EvalFile(interp, ds.string) == TCL_ERROR) {
+            errorInfo = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
+            xchat_printf(ph, "\0039Tcl plugin\003\tERROR: %s ", errorInfo);
+        } else
+            xchat_printf(ph, "\0039Tcl plugin\003\tSourced %s\n", ds.string);
+
+        Tcl_DStringFree(&ds);
+
+        complete_level--;
+
+        return XCHAT_EAT_XCHAT;
+
+    } else {
+        complete_level--;
+        return XCHAT_EAT_NONE;
+    }
+
+}
+
+static int Command_Reloadall(char *word[], char *word_eol[], void *userdata)
+{
+    Tcl_Plugin_DeInit();
+    Tcl_Plugin_Init();
+
+    xchat_print(ph, "\0039Tcl plugin\003\tRehashed\n");
+
+    return XCHAT_EAT_ALL;
+}
+
+static int TCL_Event_Handler(void *userdata)
+{
+    Tcl_DoOneEvent(TCL_DONT_WAIT);
+    do_timer();
+    return 1;
+}
+
+static void Tcl_Plugin_Init()
+{
+    int x;
+    const char *xchatdir;
+
+    interp = Tcl_CreateInterp();
+
+    Tcl_FindExecutable(NULL);
+
+    Tcl_Init(interp);
+
+    nextprocid = 0x1000;
+
+    Tcl_CreateCommand(interp, "alias", tcl_alias, NULL, NULL);
+    Tcl_CreateCommand(interp, "channels", tcl_channels, NULL, NULL);
+    Tcl_CreateCommand(interp, "chats", tcl_chats, NULL, NULL);
+    Tcl_CreateCommand(interp, "command", tcl_command, NULL, NULL);
+    Tcl_CreateCommand(interp, "complete", tcl_complete, NULL, NULL);
+    Tcl_CreateCommand(interp, "dcclist", tcl_dcclist, NULL, NULL);
+    Tcl_CreateCommand(interp, "notifylist", tcl_notifylist, NULL, NULL);
+    Tcl_CreateCommand(interp, "findcontext", tcl_findcontext, NULL, NULL);
+    Tcl_CreateCommand(interp, "getcontext", tcl_getcontext, NULL, NULL);
+    Tcl_CreateCommand(interp, "getinfo", tcl_getinfo, NULL, NULL);
+    Tcl_CreateCommand(interp, "getlist", tcl_getlist, NULL, NULL);
+    Tcl_CreateCommand(interp, "ignores", tcl_ignores, NULL, NULL);
+    Tcl_CreateCommand(interp, "killtimer", tcl_killtimer, NULL, NULL);
+    Tcl_CreateCommand(interp, "me", tcl_me, NULL, NULL);
+    Tcl_CreateCommand(interp, "on", tcl_on, NULL, NULL);
+    Tcl_CreateCommand(interp, "off", tcl_off, NULL, NULL);
+    Tcl_CreateCommand(interp, "nickcmp", tcl_xchat_nickcmp, NULL, NULL);
+    Tcl_CreateCommand(interp, "print", tcl_print, NULL, NULL);
+    Tcl_CreateCommand(interp, "prefs", tcl_prefs, NULL, NULL);
+    Tcl_CreateCommand(interp, "::puts", tcl_xchat_puts, NULL, NULL);
+    Tcl_CreateCommand(interp, "queries", tcl_queries, NULL, NULL);
+    Tcl_CreateCommand(interp, "raw", tcl_raw, NULL, NULL);
+    Tcl_CreateCommand(interp, "servers", tcl_servers, NULL, NULL);
+    Tcl_CreateCommand(interp, "setcontext", tcl_setcontext, NULL, NULL);
+    Tcl_CreateCommand(interp, "strip", tcl_strip, NULL, NULL);
+    Tcl_CreateCommand(interp, "timer", tcl_timer, NULL, NULL);
+    Tcl_CreateCommand(interp, "timerexists", tcl_timerexists, NULL, NULL);
+    Tcl_CreateCommand(interp, "timers", tcl_timers, NULL, NULL);
+    Tcl_CreateCommand(interp, "topic", tcl_topic, NULL, NULL);
+    Tcl_CreateCommand(interp, "users", tcl_users, NULL, NULL);
+    Tcl_CreateCommand(interp, "word", tcl_word, NULL, NULL);
+    Tcl_CreateCommand(interp, "word_eol", tcl_word_eol, NULL, NULL);
+
+    Tcl_InitHashTable(&cmdTablePtr, TCL_STRING_KEYS);
+    Tcl_InitHashTable(&aliasTablePtr, TCL_STRING_KEYS);
+
+    bzero(timers, sizeof(timers));
+    nexttimerid = 0;
+    nexttimerindex = 0;
+
+    for (x = 0; x < XC_SIZE; x++)
+        xc[x].hook = NULL;
+
+    xchatdir = xchat_get_info(ph, "xchatdir");
+
+    if (Tcl_Eval(interp, unknown) == TCL_ERROR) {
+        xchat_printf(ph, "Error sourcing internal 'unknown' (%s)\n", Tcl_GetStringResult(interp));
+    }
+
+    if (Tcl_Eval(interp, inlinetcl) == TCL_ERROR) {
+        xchat_printf(ph, "Error sourcing internal 'inlinetcl' (%s)\n", Tcl_GetStringResult(interp));
+    }
+
+    if (Tcl_Eval(interp, sourcedirs) == TCL_ERROR) {
+        xchat_printf(ph, "Error sourcing internal 'sourcedirs' (%s)\n", Tcl_GetStringResult(interp));
+    }
+
+}
+
+static void Tcl_Plugin_DeInit()
+{
+    int x;
+    char *procPtr;
+    alias *aliasPtr;
+    Tcl_HashEntry *entry;
+    Tcl_HashSearch search;
+
+    /* Be sure to free all the memory for ON and ALIAS entries */
+
+    entry = Tcl_FirstHashEntry(&cmdTablePtr, &search);
+    while (entry != NULL) {
+        procPtr = Tcl_GetHashValue(entry);
+        Tcl_Free(procPtr);
+        entry = Tcl_NextHashEntry(&search);
+    }
+
+    Tcl_DeleteHashTable(&cmdTablePtr);
+
+    entry = Tcl_FirstHashEntry(&aliasTablePtr, &search);
+    while (entry != NULL) {
+        aliasPtr = Tcl_GetHashValue(entry);
+        Tcl_Free(aliasPtr->procPtr);
+        if (aliasPtr->hook)
+            xchat_unhook(ph, aliasPtr->hook);
+        Tcl_Free((char *) aliasPtr);
+        entry = Tcl_NextHashEntry(&search);
+    }
+
+    Tcl_DeleteHashTable(&aliasTablePtr);
+
+    for (x = 1; x < MAX_TIMERS; x++) {
+        if (timers[x].timerid) {
+            timers[x].timerid = 0;
+            if (timers[x].procPtr != NULL)
+                Tcl_Free(timers[x].procPtr);
+            timers[x].procPtr = NULL;
+            break;
+        }
+    }
+
+    for (x = 0; x < XC_SIZE; x++) {
+        if (xc[x].hook != NULL) {
+            xchat_unhook(ph, xc[x].hook);
+            xc[x].hook = NULL;
+        }
+    }
+
+    Tcl_DeleteInterp(interp);
+}
+
+static void banner()
+{
+    xchat_printf(ph, "Tcl plugin for XChat - Version %s\n", VERSION);
+    xchat_print(ph, "Copyright 2002-2005 Daniel P. Stasinski\n");
+    xchat_print(ph, "http://www.scriptkitties.com/tclplugin/\n");
+}
+
+int xchat_plugin_init(xchat_plugin * plugin_handle, char **plugin_name, char **plugin_desc, char **plugin_version, char *arg)
+{
+#ifdef WIN32
+    HINSTANCE lib;
+#endif
+
+    strncpy(VERSION, &RCSID[19], 5);
+
+    ph = plugin_handle;
+
+#ifdef WIN32
+    lib = LoadLibraryA(TCL_DLL);
+    if (!lib) {
+        xchat_print(ph, "You must have ActiveTCL installed in order to run Tcl scripts.\n" "http://aspn.activestate.com/ASPN/Tcl/\n" "Make sure Tcl's bin directory is in your PATH.\n\n");
+        return 0;
+    }
+    FreeLibrary(lib);
+#endif
+
+    if (initialized != 0) {
+        banner();
+        xchat_print(ph, "Tcl plugin already loaded");
+        reinit_tried++;
+        return 0;
+    }
+    initialized = 1;
+
+    *plugin_name = "Tcl";
+    *plugin_desc = "Tcl scripting interface";
+    *plugin_version = VERSION;
+
+    Tcl_Plugin_Init();
+
+    raw_line_hook = xchat_hook_server(ph, "RAW LINE", XCHAT_PRI_NORM, Server_raw_line, NULL);
+    Command_TCL_hook = xchat_hook_command(ph, "tcl", XCHAT_PRI_NORM, Command_TCL, 0, 0);
+    Command_Source_hook = xchat_hook_command(ph, "source", XCHAT_PRI_NORM, Command_Source, 0, 0);
+    Command_Reload_hook = xchat_hook_command(ph, "reloadall", XCHAT_PRI_NORM, Command_Reloadall, 0, 0);
+    Command_Load_hook = xchat_hook_command(ph, "LOAD", XCHAT_PRI_NORM, Command_Source, 0, 0);
+    Event_Handler_hook = xchat_hook_timer(ph, 100, TCL_Event_Handler, 0);
+    Null_Command_hook = xchat_hook_command(ph, "", XCHAT_PRI_NORM, Null_Command_Alias, "", 0);
+
+    banner();
+    xchat_print(ph, "Tcl interface loaded\n");
+
+    return 1;                   /* return 1 for success */
+}
+
+int xchat_plugin_deinit()
+{
+    if (reinit_tried) {
+        reinit_tried--;
+        return 1;
+    }
+
+    xchat_unhook(ph, raw_line_hook);
+    xchat_unhook(ph, Command_TCL_hook);
+    xchat_unhook(ph, Command_Source_hook);
+    xchat_unhook(ph, Command_Reload_hook);
+    xchat_unhook(ph, Command_Load_hook);
+    xchat_unhook(ph, Event_Handler_hook);
+    xchat_unhook(ph, Null_Command_hook);
+
+    Tcl_Plugin_DeInit();
+
+    xchat_print(ph, "Tcl interface unloaded\n");
+    initialized = 0;
+
+    return 1;
+}
+
+void xchat_plugin_get_info(char **name, char **desc, char **version, void **reserved)
+{
+   strncpy(VERSION, &RCSID[19], 5);
+   *name = "tclplugin";
+   *desc = "Tcl plugin for XChat";
+   *version = VERSION;
+   if (reserved)
+      *reserved = NULL;
+}
+
diff --git a/plugins/tcl/tclplugin.h b/plugins/tcl/tclplugin.h
new file mode 100644
index 00000000..075c28f8
--- /dev/null
+++ b/plugins/tcl/tclplugin.h
@@ -0,0 +1,98 @@
+/***************************************************************************
+                           tclplugin.h  -  TCL plugin header file
+                           -------------------------------------------------
+    begin                : Sat Nov  9 17:31:20 MST 2002
+    copyright            : Copyright 2002-2010 Daniel P. Stasinski
+    email                : daniel@avenues.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   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.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#define BADARGS(nl,nh,example) \
+    if ((argc<(nl)) || (argc>(nh))) { \
+    Tcl_AppendResult(irp,"wrong # args: should be \"",argv[0], \
+        (example),"\"",NULL); \
+        return TCL_ERROR; \
+    }
+
+#define CHECKCTX(ctx) \
+    if (ctx == NULL) { \
+        Tcl_AppendResult(irp, "No such server/channel/nick", NULL); \
+        return TCL_ERROR; \
+    }
+
+typedef struct {
+    char *procPtr;
+    xchat_hook *hook;
+} alias;
+
+typedef struct {
+    int timerid;
+    time_t timestamp;
+    char *procPtr;
+    int count;
+    int seconds;
+} timer;
+
+typedef struct {
+    int result;
+    int defresult;
+    char **word;
+    char **word_eol;
+} t_complete;
+
+#define MAX_TIMERS 512
+#define MAX_COMPLETES 128
+
+static char *StrDup(const char *string, int *length);
+static char *myitoa(long value);
+static xchat_context *xchat_smart_context(const char *arg1, const char *arg2);
+static void queue_nexttimer();
+static int insert_timer(int seconds, int count, const char *script);
+static void do_timer();
+static int Server_raw_line(char *word[], char *word_eol[], void *userdata);
+static int Print_Hook(char *word[], void *userdata);
+static int tcl_timerexists(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[]);
+static int tcl_killtimer(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[]);
+static int tcl_timers(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[]);
+static int tcl_timer(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[]);
+static int tcl_on(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[]);
+static int tcl_off(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[]);
+static int tcl_alias(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[]);
+static int tcl_complete(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[]);
+static int tcl_raw(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[]);
+static int tcl_command(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[]);
+static int tcl_xchat_puts(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[]);
+static int tcl_print(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[]);
+static int tcl_channels(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[]);
+static int tcl_servers(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[]);
+static int tcl_queries(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[]);
+static int tcl_users(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[]);
+static int tcl_chats(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[]);
+static int tcl_ignores(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[]);
+static int tcl_dcclist(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[]);
+static int tcl_me(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[]);
+static int tcl_xchat_nickcmp(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[]);
+static int tcl_strip(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[]);
+static int tcl_topic(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[]);
+static int tcl_word(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[]);
+static int tcl_word_eol(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[]);
+static int tcl_notifylist(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[]);
+static int Command_Alias(char *word[], char *word_eol[], void *userdata);
+static int Null_Command_Alias(char *word[], char *word_eol[], void *userdata);
+static int Command_TCL(char *word[], char *word_eol[], void *userdata);
+static int Command_Source(char *word[], char *word_eol[], void *userdata);
+static int Command_Reload(char *word[], char *word_eol[], void *userdata);
+static int TCL_Event_Handler(void *userdata);
+static void Tcl_Plugin_Init();
+static void Tcl_Plugin_DeInit();
+static void banner();
+int xchat_plugin_init(xchat_plugin * plugin_handle, char **plugin_name, char **plugin_desc, char **plugin_version, char *arg);
+int xchat_plugin_deinit();