restructure configure so pkg-config derived SSL flags get used
[rofl0r-ixchat.git] / plugins / tcl / tclplugin.c
blob67948254861eb925a2a32c977b355b149a3698c8
1 /***************************************************************************
2 tclplugin.c - Tcl plugin for xchat 1.9.x / 2.x.x
3 -------------------------------------------------s
4 begin : Sat Nov 19 17:31:20 MST 2002
5 copyright : Copyright 2002-2012 Daniel P. Stasinski
6 email : daniel@GenericInbox.com
7 ***************************************************************************/
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
18 static char RCSID[] = "$Id: tclplugin.c,v 1.65 2012/07/26 20:02:12 mooooooo Exp $";
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <time.h>
24 #include <limits.h>
25 #include <ctype.h>
26 #include <stdarg.h>
27 #include <tcl.h>
28 #include <tclDecls.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
32 #include "../xchat-plugin.h"
33 #include "tclplugin.h"
34 #include "printevents.h"
36 static int nexttimerid = 0;
37 static int nexttimerindex = 0;
38 static timer timers[MAX_TIMERS];
40 static char VERSION[16];
42 static int initialized = 0;
43 static int reinit_tried = 0;
44 static Tcl_Interp *interp = NULL;
45 static xchat_plugin *ph;
46 static xchat_hook *raw_line_hook;
47 static xchat_hook *Command_TCL_hook;
48 static xchat_hook *Command_Source_hook;
49 static xchat_hook *Command_Reload_hook;
50 static xchat_hook *Command_Load_hook;
51 static xchat_hook *Event_Handler_hook;
52 static xchat_hook *Null_Command_hook;
54 static int complete_level = 0;
55 static t_complete complete[MAX_COMPLETES + 1];
56 static Tcl_HashTable cmdTablePtr;
57 static Tcl_HashTable aliasTablePtr;
59 static int nextprocid = 0x1000;
60 #define PROCPREFIX "__xctcl_"
62 static char unknown[] = {
63 "rename unknown iunknown\n"
64 "proc ::unknown {args} {\n"
65 " global errorInfo errorCode\n"
66 " if { [string index [lindex $args 0] 0] == \"/\" } {\n"
67 " command \"[string range [join $args \" \"] 1 end]\"\n"
68 " } else {\n"
69 " set code [catch {uplevel iunknown $args} msg]\n"
70 " if {$code == 1} {\n"
71 " set new [split $errorInfo \\n]\n"
72 " set new [join [lrange $new 0 [expr {[llength $new] - 8}]] \\n]\n"
73 " return -code error -errorcode $errorCode -errorinfo $new $msg\n"
74 " } else {\n"
75 " return -code $code $msg\n"
76 " }\n"
77 " }\n"
78 "}\n"
79 "proc unsupported0 {from to {bytes \"\"}} {\n"
80 " set b [expr {$bytes == \"\" ? \"\" : \"-size [list $bytes]\"}]\n"
81 " eval [list fcopy $from $to] $b\n"
82 "}\n"
85 static char sourcedirs[] = {
86 "set files [lsort [glob -nocomplain -directory [xchatdir] \"*.tcl\"]]\n"
87 "set init [lsearch -glob $files \"*/init.tcl\"]\n"
88 "if { $init > 0 } {\n"
89 "set initfile [lindex $files $init]\n"
90 "set files [lreplace $files $init $init]\n"
91 "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"
94 static char inlinetcl[] = {
95 "proc splitsrc { } {\n"
96 "uplevel \"scan \\$_src \\\"%\\\\\\[^!\\\\\\]!%\\\\\\[^@\\\\\\]@%s\\\" _nick _ident _host\"\n"
97 "}\n"
99 "proc ::exit { } {\n"
100 "puts \"Using 'exit' is bad\"\n"
101 "}\n"
103 "proc ::away { args } { return [eval [join [list getinfo $args away]]] }\n"
104 "proc ::channel { args } { return [eval [join [list getinfo $args channel]]] }\n"
105 "proc ::tab { args } { return [eval [join [list getinfo $args channel]]] }\n"
106 "proc ::charset { args } { return [eval [join [list getinfo $args charset]]] }\n"
107 "proc ::host { args } { return [eval [join [list getinfo $args host]]] }\n"
108 "proc ::inputbox { args } { return [eval [join [list getinfo $args inputbox]]] }\n"
109 "proc ::libdirfs { args } { return [eval [join [list getinfo $args libdirfs]]] }\n"
110 "proc ::network { args } { return [eval [join [list getinfo $args network]]] }\n"
111 "proc ::nickserv { args } { return [eval [join [list getinfo $args nickserv]]] }\n"
112 "proc ::server { args } { return [eval [join [list getinfo $args server]]] }\n"
113 "proc ::version { args } { return [eval [join [list getinfo $args version]]] }\n"
114 "proc ::win_status { args } { return [eval [join [list getinfo $args win_status]]] }\n"
115 "proc ::xchatdir { args } { return [eval [join [list getinfo $args xchatdir]]] }\n"
116 "proc ::xchatdirfs { args } { return [eval [join [list getinfo $args xchatdirfs]]] }\n"
118 "proc ::color { {arg {}} } { return \"\\003$arg\" }\n"
119 "proc ::bold { } { return \"\\002\" }\n"
120 "proc ::underline { } { return \"\\037\" }\n"
121 "proc ::reverse { } { return \"\\026\" }\n"
122 "proc ::reset { } { return \"\\017\" }\n"
124 "proc ::__xctcl_errorInfo { } {\n"
125 " set text [split $::errorInfo \\n]\n"
126 " puts [string trim [join [lrange $text 0 [expr {[llength $text] - 4}]] \\n]]\n"
127 "}\n"
129 "proc ::bgerror { message } {\n"
130 " set text [split $::errorInfo \\n]\n"
131 " puts [string trim [join [lrange $text 0 [expr {[llength $text] - 4}]] \\n]]\n"
132 "}\n"
135 static void NiceErrorInfo ()
137 Tcl_Eval(interp, "::__xctcl_errorInfo");
140 static void Tcl_MyDStringAppend(Tcl_DString * ds, char *string)
142 Tcl_DStringAppend(ds, string, strlen(string));
145 static char *InternalProcName(int value)
147 static char result[32];
148 sprintf(result, "%s%08x", PROCPREFIX, value);
149 return result;
152 static int SourceInternalProc(int id, const char *args, const char *source)
154 Tcl_DString ds;
155 int result;
156 Tcl_DStringInit(&ds);
158 Tcl_MyDStringAppend(&ds, "proc ");
159 Tcl_MyDStringAppend(&ds, InternalProcName(id));
160 Tcl_MyDStringAppend(&ds, " { ");
161 Tcl_MyDStringAppend(&ds, (char *) args);
162 Tcl_MyDStringAppend(&ds, " } {\n");
163 Tcl_MyDStringAppend(&ds, (char *) source);
164 Tcl_MyDStringAppend(&ds, "\n}\n\n");
166 result = Tcl_Eval(interp, ds.string);
168 Tcl_DStringFree(&ds);
170 return result;
173 static int EvalInternalProc(const char *procname, int ct, ...)
175 Tcl_DString ds;
176 int result;
177 va_list ap;
178 char *buf;
180 Tcl_DStringInit(&ds);
182 Tcl_MyDStringAppend(&ds, (char *) procname);
184 if (ct) {
185 va_start(ap, ct);
186 while (ct--) {
187 if ((buf = va_arg(ap, char *)) != NULL)
188 Tcl_DStringAppendElement(&ds, buf);
189 else
190 Tcl_MyDStringAppend(&ds, " \"\"");
192 va_end(ap);
195 result = Tcl_Eval(interp, ds.string);
197 Tcl_DStringFree(&ds);
199 return result;
203 static void DeleteInternalProc(const char *proc)
205 Tcl_DString ds;
207 Tcl_DStringInit(&ds);
208 Tcl_MyDStringAppend(&ds, "rename ");
209 Tcl_MyDStringAppend(&ds, (char *) proc);
210 Tcl_MyDStringAppend(&ds, " {}");
211 Tcl_Eval(interp, ds.string);
212 Tcl_DStringFree(&ds);
215 static char *StrDup(const char *string, int *length)
217 char *result;
219 if (string == NULL)
220 return NULL;
222 *length = strlen(string);
223 result = Tcl_Alloc(*length + 1);
224 strncpy(result, string, *length);
225 result[*length] = 0;
227 return result;
230 static char *myitoa(long value)
232 static char result[32];
233 sprintf(result, "%ld", value);
234 return result;
237 static xchat_context *atoctx(const char *nptr)
239 int isnum = 1;
240 int x = 0;
242 if (!nptr)
243 return NULL;
245 while (isnum && nptr[x]) {
246 if (!isdigit(nptr[x++]))
247 isnum--;
250 if (isnum && x)
251 return (xchat_context *) atol(nptr);
252 else
253 return NULL;
256 static xchat_context *xchat_smart_context(const char *arg1, const char *arg2)
258 const char *server, *s, *n;
259 xchat_context *result = NULL;
260 xchat_context *ctx = NULL;
261 xchat_context *actx = NULL;
262 xchat_list *list;
264 if (arg1 == NULL)
265 return xchat_get_context(ph);;
267 if (arg1 && arg2) {
268 result = xchat_find_context(ph, arg1, arg2);
269 if (result == NULL)
270 result = xchat_find_context(ph, arg2, arg1);
271 return result;
272 } else {
274 actx = atoctx(arg1);
276 server = xchat_get_info(ph, "server");
278 list = xchat_list_get(ph, "channels");
280 if (list != NULL) {
282 while (xchat_list_next(ph, list)) {
284 ctx = (xchat_context *)xchat_list_str(ph, list, "context");
286 if (actx) {
287 if (ctx == actx) {
288 result = ctx;
289 break;
291 } else {
293 s = xchat_list_str(ph, list, "server");
295 if (xchat_list_int(ph, list, "type") == 1) {
296 if (strcasecmp(arg1, s) == 0) {
297 result = ctx;
298 break;
300 n = xchat_list_str(ph, list, "network");
301 if (n) {
302 if (strcasecmp(arg1, n) == 0) {
303 result = ctx;
304 break;
307 } else {
308 if ((strcasecmp(server, s) == 0) && (strcasecmp(arg1, xchat_list_str(ph, list, "channel")) == 0)) {
309 result = ctx;
310 break;
316 xchat_list_free(ph, list);
321 return result;
324 static void queue_nexttimer()
326 int x;
327 time_t then;
329 nexttimerindex = 0;
330 then = (time_t) 0x7fffffff;
332 for (x = 1; x < MAX_TIMERS; x++) {
333 if (timers[x].timerid) {
334 if (timers[x].timestamp < then) {
335 then = timers[x].timestamp;
336 nexttimerindex = x;
342 static int insert_timer(int seconds, int count, const char *script)
344 int x;
345 int dummy;
346 time_t now;
347 int id;
349 if (script == NULL)
350 return (-1);
352 id = (nextprocid++ % INT_MAX) + 1;
354 now = time(NULL);
356 for (x = 1; x < MAX_TIMERS; x++) {
357 if (timers[x].timerid == 0) {
358 if (SourceInternalProc(id, "", script) == TCL_ERROR) {
359 xchat_printf(ph, "\0039TCL plugin\003\tERROR (timer %d) ", timers[x].timerid);
360 NiceErrorInfo ();
361 return (-1);
363 timers[x].timerid = (nexttimerid++ % INT_MAX) + 1;
364 timers[x].timestamp = now + seconds;
365 timers[x].count = count;
366 timers[x].seconds = seconds;
367 timers[x].procPtr = StrDup(InternalProcName(id), &dummy);
368 queue_nexttimer();
369 return (timers[x].timerid);
373 return (-1);
376 static void do_timer()
378 xchat_context *origctx;
379 time_t now;
380 int index;
382 if (!nexttimerindex)
383 return;
385 now = time(NULL);
387 if (now < timers[nexttimerindex].timestamp)
388 return;
390 index = nexttimerindex;
391 origctx = xchat_get_context(ph);
392 if (EvalInternalProc(timers[index].procPtr, 0) == TCL_ERROR) {
393 xchat_printf(ph, "\0039TCL plugin\003\tERROR (timer %d) ", timers[index].timerid);
394 NiceErrorInfo ();
396 xchat_set_context(ph, origctx);
398 if (timers[index].count != -1)
399 timers[index].count--;
401 if (timers[index].count == 0) {
402 timers[index].timerid = 0;
403 if (timers[index].procPtr != NULL) {
404 DeleteInternalProc(timers[index].procPtr);
405 Tcl_Free(timers[index].procPtr);
407 timers[index].procPtr = NULL;
408 } else {
409 timers[index].timestamp += timers[index].seconds;
412 queue_nexttimer();
414 return;
418 static int Server_raw_line(char *word[], char *word_eol[], void *userdata)
420 char *src, *cmd, *dest, *rest;
421 char *chancmd = NULL;
422 char *procList;
423 Tcl_HashEntry *entry = NULL;
424 xchat_context *origctx;
425 int len;
426 int dummy;
427 char *string = NULL;
428 int ctcp = 0;
429 int count;
430 int list_argc, proc_argc;
431 const char **list_argv, **proc_argv;
432 int private = 0;
434 if (word[0][0] == 0)
435 return XCHAT_EAT_NONE;
437 if (complete_level == MAX_COMPLETES)
438 return XCHAT_EAT_NONE;
440 complete_level++;
441 complete[complete_level].defresult = XCHAT_EAT_NONE; /* XCHAT_EAT_PLUGIN; */
442 complete[complete_level].result = XCHAT_EAT_NONE;
443 complete[complete_level].word = word;
444 complete[complete_level].word_eol = word_eol;
446 if (word[1][0] == ':') {
447 src = word[1];
448 cmd = word[2];
449 dest = word[3];
450 rest = word_eol[4];
451 } else {
452 src = "";
453 cmd = word[1];
454 if (word_eol[2][0] == ':') {
455 dest = "";
456 rest = word_eol[2];
457 } else {
458 dest = word[2];
459 rest = word_eol[3];
463 if (src[0] == ':')
464 src++;
465 if (dest[0] == ':')
466 dest++;
467 if (rest[0] == ':')
468 rest++;
470 if (rest[0] == 0x01) {
471 rest++;
472 if (strcasecmp("PRIVMSG", cmd) == 0) {
473 if (strncasecmp(rest, "ACTION ", 7) == 0) {
474 cmd = "ACTION";
475 rest += 7;
476 } else
477 cmd = "CTCP";
478 } else if (!strcasecmp("NOTICE", cmd))
479 cmd = "CTCR";
480 ctcp = 1;
481 } else if (!strcasecmp("NOTICE", cmd) && (strchr(src, '!') == NULL)) {
482 cmd = "SNOTICE";
483 } else if (rest[0] == '!') {
484 chancmd = word[4] + 1;
487 if (chancmd != NULL) {
488 string = StrDup(chancmd, &dummy);
489 Tcl_UtfToUpper(string);
490 if ((entry = Tcl_FindHashEntry(&cmdTablePtr, string)) == NULL) {
491 Tcl_Free(string);
492 } else {
493 cmd = chancmd;
494 rest = word_eol[5];
498 if (entry == NULL) {
499 string = StrDup(cmd, &dummy);
500 Tcl_UtfToUpper(string);
501 entry = Tcl_FindHashEntry(&cmdTablePtr, string);
504 if (entry != NULL) {
506 procList = Tcl_GetHashValue(entry);
508 if (isalpha(dest[0]))
509 private = 1;
511 rest = StrDup(rest, &len);
512 if (ctcp) {
513 if (rest != NULL) {
514 if ((len > 1) && (rest[len - 1] == 0x01))
515 rest[len - 1] = 0;
519 if (Tcl_SplitList(interp, procList, &list_argc, &list_argv) == TCL_OK) {
521 for (count = 0; count < list_argc; count++) {
523 if (Tcl_SplitList(interp, list_argv[count], &proc_argc, &proc_argv) != TCL_OK)
524 continue;
526 origctx = xchat_get_context(ph);
527 if (EvalInternalProc(proc_argv[1], 7, src, dest, cmd, rest, word_eol[1], proc_argv[0], myitoa(private)) == TCL_ERROR) {
528 xchat_printf(ph, "\0039TCL plugin\003\tERROR (on %s %s) ", cmd, proc_argv[0]);
529 NiceErrorInfo ();
531 xchat_set_context(ph, origctx);
533 Tcl_Free((char *) proc_argv);
535 if ((complete[complete_level].result == XCHAT_EAT_PLUGIN) || (complete[complete_level].result == XCHAT_EAT_ALL))
536 break;
540 Tcl_Free((char *) list_argv);
544 Tcl_Free(rest);
548 if (string)
549 Tcl_Free(string);
551 return complete[complete_level--].result;
555 static int Print_Hook(char *word[], void *userdata)
557 char *procList;
558 Tcl_HashEntry *entry;
559 xchat_context *origctx;
560 int count;
561 int list_argc, proc_argc;
562 const char **list_argv, **proc_argv;
563 Tcl_DString ds;
564 int x;
566 if (complete_level == MAX_COMPLETES)
567 return XCHAT_EAT_NONE;
569 complete_level++;
570 complete[complete_level].defresult = XCHAT_EAT_NONE; /* XCHAT_EAT_PLUGIN; */
571 complete[complete_level].result = XCHAT_EAT_NONE;
572 complete[complete_level].word = word;
573 complete[complete_level].word_eol = word;
575 if ((entry = Tcl_FindHashEntry(&cmdTablePtr, xc[(intptr_t) userdata].event)) != NULL) {
577 procList = Tcl_GetHashValue(entry);
579 if (Tcl_SplitList(interp, procList, &list_argc, &list_argv) == TCL_OK) {
581 for (count = 0; count < list_argc; count++) {
583 if (Tcl_SplitList(interp, list_argv[count], &proc_argc, &proc_argv) != TCL_OK)
584 continue;
586 origctx = xchat_get_context(ph);
588 Tcl_DStringInit(&ds);
590 if ((intptr_t) userdata == CHAT) {
591 Tcl_DStringAppend(&ds, word[3], strlen(word[3]));
592 Tcl_DStringAppend(&ds, "!*@", 3);
593 Tcl_DStringAppend(&ds, word[1], strlen(word[1]));
594 if (EvalInternalProc(proc_argv[1], 7, ds.string, word[2], xc[(intptr_t) userdata].event, word[4], "", proc_argv[0], "0") == TCL_ERROR) {
595 xchat_printf(ph, "\0039TCL plugin\003\tERROR (on %s %s) ", xc[(intptr_t) userdata].event, proc_argv[0]);
596 NiceErrorInfo ();
598 } else {
599 if (xc[(intptr_t) userdata].argc > 0) {
600 for (x = 0; x <= xc[(intptr_t) userdata].argc; x++)
601 Tcl_DStringAppendElement(&ds, word[x]);
603 if (EvalInternalProc(proc_argv[1], 7, "", "", xc[(intptr_t) userdata].event, "", ds.string, proc_argv[0], "0") == TCL_ERROR) {
604 xchat_printf(ph, "\0039Tcl plugin\003\tERROR (on %s %s) ", xc[(intptr_t) userdata].event, proc_argv[0]);
605 NiceErrorInfo ();
609 Tcl_DStringFree(&ds);
611 xchat_set_context(ph, origctx);
613 Tcl_Free((char *) proc_argv);
615 if ((complete[complete_level].result == XCHAT_EAT_PLUGIN) || (complete[complete_level].result == XCHAT_EAT_ALL))
616 break;
620 Tcl_Free((char *) list_argv);
625 return complete[complete_level--].result;
629 static int tcl_timerexists(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
631 int x;
632 int timerid;
634 BADARGS(2, 2, " schedid");
636 if (Tcl_GetInt(irp, argv[1], &timerid) != TCL_OK) {
637 Tcl_AppendResult(irp, "Invalid timer id", NULL);
638 return TCL_ERROR;
641 if (timerid) {
642 for (x = 1; x < MAX_TIMERS; x++) {
643 if (timers[x].timerid == timerid) {
644 Tcl_AppendResult(irp, "1", NULL);
645 return TCL_OK;
650 Tcl_AppendResult(irp, "0", NULL);
652 return TCL_OK;
655 static int tcl_killtimer(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
657 int x;
658 int timerid;
660 BADARGS(2, 2, " timerid");
662 if (Tcl_GetInt(irp, argv[1], &timerid) != TCL_OK) {
663 Tcl_AppendResult(irp, "Invalid timer id", NULL);
664 return TCL_ERROR;
667 if (timerid) {
668 for (x = 1; x < MAX_TIMERS; x++) {
669 if (timers[x].timerid == timerid) {
670 timers[x].timerid = 0;
671 if (timers[x].procPtr != NULL) {
672 DeleteInternalProc(timers[x].procPtr);
673 Tcl_Free(timers[x].procPtr);
675 timers[x].procPtr = NULL;
676 break;
681 queue_nexttimer();
683 return TCL_OK;
686 static int tcl_timers(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
688 int x;
689 Tcl_DString ds;
690 time_t now;
692 BADARGS(1, 1, "");
694 now = time(NULL);
696 Tcl_DStringInit(&ds);
698 for (x = 1; x < MAX_TIMERS; x++) {
699 if (timers[x].timerid) {
700 Tcl_DStringStartSublist(&ds);
701 Tcl_DStringAppendElement(&ds, myitoa((long)timers[x].timerid));
702 Tcl_DStringAppendElement(&ds, myitoa((long)timers[x].timestamp - now));
703 Tcl_DStringAppendElement(&ds, timers[x].procPtr);
704 Tcl_DStringAppendElement(&ds, myitoa((long)timers[x].seconds));
705 Tcl_DStringAppendElement(&ds, myitoa((long)timers[x].count));
706 Tcl_DStringEndSublist(&ds);
710 Tcl_AppendResult(interp, ds.string, NULL);
711 Tcl_DStringFree(&ds);
713 return TCL_OK;
716 static int tcl_timer(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
718 int seconds;
719 int timerid;
720 int repeat = 0;
721 int count = 0;
722 int first = 1;
723 char reply[32];
725 BADARGS(3, 6, " ?-repeat? ?-count times? seconds {script | procname ?args?}");
727 while (argc--) {
728 if (strcasecmp(argv[first], "-repeat") == 0) {
729 repeat++;
730 } else if (strcasecmp(argv[first], "-count") == 0) {
731 if (Tcl_GetInt(irp, argv[++first], &count) != TCL_OK)
732 return TCL_ERROR;
733 } else {
734 break;
736 first++;
739 if (repeat && !count)
740 count = -1;
742 if (!count)
743 count = 1;
745 if (Tcl_GetInt(irp, argv[first++], &seconds) != TCL_OK)
746 return TCL_ERROR;
748 if ((timerid = insert_timer(seconds, count, argv[first++])) == -1) {
749 Tcl_AppendResult(irp, "0", NULL);
750 return TCL_ERROR;
753 sprintf(reply, "%d", timerid);
755 Tcl_AppendResult(irp, reply, NULL);
757 queue_nexttimer();
759 return TCL_OK;
762 static int tcl_on(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
764 int newentry;
765 char *procList;
766 Tcl_HashEntry *entry;
767 char *token;
768 int dummy;
769 Tcl_DString ds;
770 size_t index;
771 int count;
772 int list_argc, proc_argc;
773 int id;
774 const char **list_argv, **proc_argv;
776 BADARGS(4, 4, " token label {script | procname ?args?}");
778 id = (nextprocid++ % INT_MAX) + 1;
780 if (SourceInternalProc(id, "_src _dest _cmd _rest _raw _label _private", argv[3]) == TCL_ERROR) {
781 xchat_printf(ph, "\0039Tcl plugin\003\tERROR (on %s:%s) ", argv[1], argv[2]);
782 NiceErrorInfo ();
783 return TCL_OK;
786 token = StrDup(argv[1], &dummy);
787 Tcl_UtfToUpper(token);
789 Tcl_DStringInit(&ds);
791 entry = Tcl_CreateHashEntry(&cmdTablePtr, token, &newentry);
792 if (!newentry) {
793 procList = Tcl_GetHashValue(entry);
794 if (Tcl_SplitList(interp, procList, &list_argc, &list_argv) == TCL_OK) {
795 for (count = 0; count < list_argc; count++) {
796 if (Tcl_SplitList(interp, list_argv[count], &proc_argc, &proc_argv) != TCL_OK)
797 continue;
798 if (strcmp(proc_argv[0], argv[2])) {
799 Tcl_DStringStartSublist(&ds);
800 Tcl_DStringAppendElement(&ds, proc_argv[0]);
801 Tcl_DStringAppendElement(&ds, proc_argv[1]);
802 Tcl_DStringEndSublist(&ds);
803 } else {
804 DeleteInternalProc(proc_argv[1]);
806 Tcl_Free((char *) proc_argv);
808 Tcl_Free((char *) list_argv);
810 Tcl_Free(procList);
813 Tcl_DStringStartSublist(&ds);
814 Tcl_DStringAppendElement(&ds, argv[2]);
815 Tcl_DStringAppendElement(&ds, InternalProcName(id));
816 Tcl_DStringEndSublist(&ds);
818 procList = StrDup(ds.string, &dummy);
820 Tcl_SetHashValue(entry, procList);
822 if ((strncmp(token, "XC_", 3) == 0) || (strcmp(token, "CHAT") == 0)) {
823 for (index = 0; index < XC_SIZE; index++) {
824 if (strcmp(xc[index].event, token) == 0) {
825 if (xc[index].hook == NULL) {
826 xc[index].hook = xchat_hook_print(ph, xc[index].emit, XCHAT_PRI_NORM, Print_Hook, (void *) index);
827 break;
833 Tcl_Free(token);
834 Tcl_DStringFree(&ds);
836 return TCL_OK;
839 static int tcl_off(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
841 char *procList;
842 Tcl_HashEntry *entry;
843 char *token;
844 int dummy;
845 Tcl_DString ds;
846 int index;
847 int count;
848 int list_argc, proc_argc;
849 const char **list_argv, **proc_argv;
851 BADARGS(2, 3, " token ?label?");
853 token = StrDup(argv[1], &dummy);
854 Tcl_UtfToUpper(token);
856 Tcl_DStringInit(&ds);
858 if ((entry = Tcl_FindHashEntry(&cmdTablePtr, token)) != NULL) {
860 procList = Tcl_GetHashValue(entry);
862 if (argc == 3) {
863 if (Tcl_SplitList(interp, procList, &list_argc, &list_argv) == TCL_OK) {
864 for (count = 0; count < list_argc; count++) {
865 if (Tcl_SplitList(interp, list_argv[count], &proc_argc, &proc_argv) != TCL_OK)
866 continue;
867 if (strcmp(proc_argv[0], argv[2])) {
868 Tcl_DStringStartSublist(&ds);
869 Tcl_DStringAppendElement(&ds, proc_argv[0]);
870 Tcl_DStringAppendElement(&ds, proc_argv[1]);
871 Tcl_DStringEndSublist(&ds);
872 } else
873 DeleteInternalProc(proc_argv[1]);
874 Tcl_Free((char *) proc_argv);
876 Tcl_Free((char *) list_argv);
880 Tcl_Free(procList);
882 if (ds.length) {
883 procList = StrDup(ds.string, &dummy);
884 Tcl_SetHashValue(entry, procList);
885 } else {
886 Tcl_DeleteHashEntry(entry);
889 if (!ds.length) {
890 if ((strncmp(token, "XC_", 3) == 0) || (strcmp(token, "CHAT") == 0)) {
891 for (index = 0; index < XC_SIZE; index++) {
892 if (strcmp(xc[index].event, token) == 0) {
893 if (xc[index].hook != NULL) {
894 xchat_unhook(ph, xc[index].hook);
895 xc[index].hook = NULL;
896 break;
904 Tcl_Free(token);
905 Tcl_DStringFree(&ds);
907 return TCL_OK;
910 static int tcl_alias(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
912 int newentry;
913 alias *aliasPtr;
914 Tcl_HashEntry *entry;
915 char *string;
916 const char *help = NULL;
917 int dummy;
918 int id;
920 BADARGS(3, 4, " name ?help? {script | procname ?args?}");
922 string = StrDup(argv[1], &dummy);
923 Tcl_UtfToUpper(string);
925 if (strlen(argv[argc - 1])) {
927 if (argc == 4)
928 help = argv[2];
930 id = (nextprocid++ % INT_MAX) + 1;
932 if (SourceInternalProc(id, "_cmd _rest", argv[argc - 1]) == TCL_ERROR) {
933 xchat_printf(ph, "\0039Tcl plugin\003\tERROR (alias %s) ", argv[1]);
934 NiceErrorInfo ();
935 return TCL_OK;
938 entry = Tcl_CreateHashEntry(&aliasTablePtr, string, &newentry);
939 if (newentry) {
940 aliasPtr = (alias *) Tcl_Alloc(sizeof(alias));
941 if (string[0] == '@')
942 aliasPtr->hook = NULL;
943 else
944 aliasPtr->hook = xchat_hook_command(ph, string, XCHAT_PRI_NORM, Command_Alias, help, 0);
945 } else {
946 aliasPtr = Tcl_GetHashValue(entry);
947 DeleteInternalProc(aliasPtr->procPtr);
948 Tcl_Free(aliasPtr->procPtr);
951 aliasPtr->procPtr = StrDup(InternalProcName(id), &dummy);
953 Tcl_SetHashValue(entry, aliasPtr);
955 } else {
957 if ((entry = Tcl_FindHashEntry(&aliasTablePtr, string)) != NULL) {
958 aliasPtr = Tcl_GetHashValue(entry);
959 DeleteInternalProc(aliasPtr->procPtr);
960 Tcl_Free(aliasPtr->procPtr);
961 if (aliasPtr->hook)
962 xchat_unhook(ph, aliasPtr->hook);
963 Tcl_Free((char *) aliasPtr);
964 Tcl_DeleteHashEntry(entry);
968 Tcl_Free(string);
970 return TCL_OK;
973 static int tcl_complete(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
975 BADARGS(1, 2, " ?EAT_NONE|EAT_XCHAT|EAT_PLUGIN|EAT_ALL?");
977 if (argc == 2) {
978 if (Tcl_GetInt(irp, argv[1], &complete[complete_level].result) != TCL_OK) {
979 if (strcasecmp("EAT_NONE", argv[1]) == 0)
980 complete[complete_level].result = XCHAT_EAT_NONE;
981 else if (strcasecmp("EAT_XCHAT", argv[1]) == 0)
982 complete[complete_level].result = XCHAT_EAT_XCHAT;
983 else if (strcasecmp("EAT_PLUGIN", argv[1]) == 0)
984 complete[complete_level].result = XCHAT_EAT_PLUGIN;
985 else if (strcasecmp("EAT_ALL", argv[1]) == 0)
986 complete[complete_level].result = XCHAT_EAT_ALL;
987 else
988 BADARGS(1, 2, " ?EAT_NONE|EAT_XCHAT|EAT_PLUGIN|EAT_ALL?");
990 } else
991 complete[complete_level].result = complete[complete_level].defresult;
993 return TCL_RETURN;
996 static int tcl_command(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
998 xchat_context *origctx;
999 xchat_context *ctx = NULL;
1000 const char *string = NULL;
1002 BADARGS(2, 4, " ?server|network|context? ?#channel|nick? text");
1004 origctx = xchat_get_context(ph);
1006 switch (argc) {
1007 case 2:
1008 ctx = origctx;
1009 break;
1010 case 3:
1011 ctx = xchat_smart_context(argv[1], NULL);
1012 break;
1013 case 4:
1014 ctx = xchat_smart_context(argv[1], argv[2]);
1015 break;
1016 default:;
1019 CHECKCTX(ctx);
1021 string = argv[argc - 1];
1023 if (string[0] == '/')
1024 string++;
1026 xchat_set_context(ph, ctx);
1027 xchat_command(ph, string);
1028 xchat_set_context(ph, origctx);
1030 return TCL_OK;
1033 static int tcl_raw(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
1035 xchat_context *origctx;
1036 xchat_context *ctx = NULL;
1037 const char *string = NULL;
1039 BADARGS(2, 4, " ?server|network|context? ?#channel|nick? text");
1041 origctx = xchat_get_context(ph);
1043 switch (argc) {
1044 case 2:
1045 ctx = origctx;
1046 break;
1047 case 3:
1048 ctx = xchat_smart_context(argv[1], NULL);
1049 break;
1050 case 4:
1051 ctx = xchat_smart_context(argv[1], argv[2]);
1052 break;
1053 default:;
1056 CHECKCTX(ctx);
1058 string = argv[argc - 1];
1060 xchat_set_context(ph, ctx);
1061 xchat_commandf(ph, "RAW %s", string);
1062 xchat_set_context(ph, origctx);
1064 return TCL_OK;
1068 static int tcl_prefs(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
1070 int i;
1071 const char *str;
1073 BADARGS(2, 2, " name");
1075 switch (xchat_get_prefs (ph, argv[1], &str, &i)) {
1076 case 1:
1077 Tcl_AppendResult(irp, str, NULL);
1078 break;
1079 case 2:
1080 case 3:
1081 Tcl_AppendResult(irp, myitoa(i), NULL);
1082 break;
1083 default:
1084 Tcl_AppendResult(irp, NULL);
1087 return TCL_OK;
1090 static int tcl_info(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[], char *id)
1092 char *result;
1093 int max_argc;
1094 xchat_context *origctx, *ctx;
1096 if (id == NULL) {
1097 BADARGS(2, 3, " ?server|network|context? id");
1098 max_argc = 3;
1099 } else {
1100 BADARGS(1, 2, " ?server|network|context?");
1101 max_argc = 2;
1104 origctx = xchat_get_context(ph);
1106 if (argc == max_argc) {
1107 ctx = xchat_smart_context(argv[1], NULL);
1108 CHECKCTX(ctx);
1109 xchat_set_context(ph, ctx);
1112 if (id == NULL)
1113 id = (char *) argv[argc-1];
1115 if ((result = (char *) xchat_get_info(ph, id)) == NULL)
1116 result = "";
1118 Tcl_AppendResult(irp, result, NULL);
1120 xchat_set_context(ph, origctx);
1122 return TCL_OK;
1125 static int tcl_me(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
1127 return tcl_info(cd, irp, argc, argv, "nick");
1130 static int tcl_getinfo(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
1132 return tcl_info(cd, irp, argc, argv, NULL);
1135 static int tcl_getlist(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
1137 xchat_list *list = NULL;
1138 const char *name = NULL;
1139 const char * const *fields;
1140 const char *field;
1141 const char *sattr;
1142 int iattr;
1143 int i;
1144 time_t t;
1145 Tcl_DString ds;
1146 xchat_context *origctx;
1147 xchat_context *ctx = NULL;
1149 origctx = xchat_get_context(ph);
1151 BADARGS(1, 2, " list");
1153 Tcl_DStringInit(&ds);
1155 fields = xchat_list_fields(ph, "lists");
1157 if (argc == 1) {
1158 for (i = 0; fields[i] != NULL; i++) {
1159 Tcl_DStringAppendElement(&ds, fields[i]);
1161 goto done;
1164 for (i = 0; fields[i] != NULL; i++) {
1165 if (strcmp(fields[i], argv[1]) == 0) {
1166 name = fields[i];
1167 break;
1171 if (name == NULL)
1172 goto done;
1174 list = xchat_list_get(ph, name);
1175 if (list == NULL)
1176 goto done;
1178 fields = xchat_list_fields(ph, name);
1180 Tcl_DStringStartSublist(&ds);
1181 for (i = 0; fields[i] != NULL; i++) {
1182 field = fields[i] + 1;
1183 Tcl_DStringAppendElement(&ds, field);
1185 Tcl_DStringEndSublist(&ds);
1187 while (xchat_list_next(ph, list)) {
1189 Tcl_DStringStartSublist(&ds);
1191 for (i = 0; fields[i] != NULL; i++) {
1193 field = fields[i] + 1;
1195 switch (fields[i][0]) {
1196 case 's':
1197 sattr = xchat_list_str(ph, list, (char *) field);
1198 Tcl_DStringAppendElement(&ds, sattr);
1199 break;
1200 case 'i':
1201 iattr = xchat_list_int(ph, list, (char *) field);
1202 Tcl_DStringAppendElement(&ds, myitoa((long)iattr));
1203 break;
1204 case 't':
1205 t = xchat_list_time(ph, list, (char *) field);
1206 Tcl_DStringAppendElement(&ds, myitoa((long)t));
1207 break;
1208 case 'p':
1209 sattr = xchat_list_str(ph, list, (char *) field);
1210 if (strcmp(field, "context") == 0) {
1211 ctx = (xchat_context *) sattr;
1212 Tcl_DStringAppendElement(&ds, myitoa((long)ctx));
1213 } else
1214 Tcl_DStringAppendElement(&ds, "*");
1215 break;
1216 default:
1217 Tcl_DStringAppendElement(&ds, "*");
1218 break;
1222 Tcl_DStringEndSublist(&ds);
1226 xchat_list_free(ph, list);
1228 done:
1230 xchat_set_context(ph, origctx);
1232 Tcl_AppendResult(irp, ds.string, NULL);
1234 Tcl_DStringFree(&ds);
1236 return TCL_OK;
1240 * tcl_xchat_puts - stub for tcl puts command
1241 * This is modified from the original internal "puts" command. It redirects
1242 * stdout to the current context, while still allowing all normal puts features
1245 static int tcl_xchat_puts(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
1247 Tcl_Channel chan;
1248 const char *string;
1249 int newline;
1250 const char *channelId = NULL;
1251 int result;
1252 int mode;
1253 int trap_stdout = 0;
1255 switch (argc) {
1257 case 2:
1258 string = argv[1];
1259 newline = 1;
1260 trap_stdout = 1;
1261 break;
1263 case 3:
1264 if (strcmp(argv[1], "-nonewline") == 0) {
1265 newline = 0;
1266 trap_stdout = 1;
1267 } else {
1268 newline = 1;
1269 channelId = argv[1];
1271 string = argv[2];
1272 break;
1274 case 4:
1275 if (strcmp(argv[1], "-nonewline") == 0) {
1276 channelId = argv[2];
1277 string = argv[3];
1278 } else {
1279 if (strcmp(argv[3], "nonewline") != 0) {
1280 Tcl_AppendResult(interp, "bad argument \"", argv[3], "\": should be \"nonewline\"", (char *) NULL);
1281 return TCL_ERROR;
1283 channelId = argv[1];
1284 string = argv[2];
1286 newline = 0;
1287 break;
1289 default:
1290 Tcl_AppendResult(interp, argv, "?-nonewline? ?channelId? string", NULL);
1291 return TCL_ERROR;
1294 if (!trap_stdout && (strcmp(channelId, "stdout") == 0))
1295 trap_stdout = 1;
1297 if (trap_stdout) {
1298 if (newline)
1299 xchat_printf(ph, "%s\n", string);
1300 else
1301 xchat_print(ph, string);
1302 return TCL_OK;
1305 chan = Tcl_GetChannel(interp, channelId, &mode);
1306 if (chan == (Tcl_Channel) NULL) {
1307 return TCL_ERROR;
1309 if ((mode & TCL_WRITABLE) == 0) {
1310 Tcl_AppendResult(interp, "channel \"", channelId, "\" wasn't opened for writing", (char *) NULL);
1311 return TCL_ERROR;
1314 result = Tcl_Write(chan, string, strlen(string));
1315 if (result < 0) {
1316 goto error;
1318 if (newline != 0) {
1319 result = Tcl_WriteChars(chan, "\n", 1);
1320 if (result < 0) {
1321 goto error;
1324 return TCL_OK;
1326 error:
1327 Tcl_AppendResult(interp, "error writing \"", channelId, "\": ", Tcl_PosixError(interp), (char *) NULL);
1329 return TCL_ERROR;
1332 static int tcl_print(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
1334 xchat_context *origctx;
1335 xchat_context *ctx = NULL;
1336 const char *string = NULL;
1338 BADARGS(2, 4, " ?server|network|context? ?#channel|nick? text");
1340 origctx = xchat_get_context(ph);
1342 switch (argc) {
1343 case 2:
1344 ctx = origctx;
1345 break;
1346 case 3:
1347 ctx = xchat_smart_context(argv[1], NULL);
1348 break;
1349 case 4:
1350 ctx = xchat_smart_context(argv[1], argv[2]);
1351 break;
1352 default:;
1355 CHECKCTX(ctx);
1357 string = argv[argc - 1];
1359 xchat_set_context(ph, ctx);
1360 xchat_print(ph, string);
1361 xchat_set_context(ph, origctx);
1363 return TCL_OK;
1366 static int tcl_setcontext(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
1368 xchat_context *ctx = NULL;
1370 BADARGS(2, 2, " context");
1372 ctx = xchat_smart_context(argv[1], NULL);
1374 CHECKCTX(ctx);
1376 xchat_set_context(ph, ctx);
1378 return TCL_OK;
1381 static int tcl_findcontext(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
1383 xchat_context *ctx = NULL;
1385 BADARGS(1, 3, " ?server|network|context? ?channel?");
1387 switch (argc) {
1388 case 1:
1389 ctx = xchat_find_context(ph, NULL, NULL);
1390 break;
1391 case 2:
1392 ctx = xchat_smart_context(argv[1], NULL);
1393 break;
1394 case 3:
1395 ctx = xchat_smart_context(argv[1], argv[2]);
1396 break;
1397 default:;
1400 CHECKCTX(ctx);
1402 Tcl_AppendResult(irp, myitoa((long)ctx), NULL);
1404 return TCL_OK;
1407 static int tcl_getcontext(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
1409 xchat_context *ctx = NULL;
1411 BADARGS(1, 1, "");
1413 ctx = xchat_get_context(ph);
1415 Tcl_AppendResult(irp, myitoa((long)ctx), NULL);
1417 return TCL_OK;
1420 static int tcl_channels(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
1422 const char *server, *channel;
1423 xchat_list *list;
1424 Tcl_DString ds;
1425 xchat_context *origctx;
1426 xchat_context *ctx;
1428 origctx = xchat_get_context(ph);
1430 BADARGS(1, 2, " ?server|network|context?");
1432 if (argc == 2) {
1433 ctx = xchat_smart_context(argv[1], NULL);
1434 CHECKCTX(ctx);
1435 xchat_set_context(ph, ctx);
1438 server = (char *) xchat_get_info(ph, "server");
1440 Tcl_DStringInit(&ds);
1442 list = xchat_list_get(ph, "channels");
1444 if (list != NULL) {
1445 while (xchat_list_next(ph, list)) {
1446 if (xchat_list_int(ph, list, "type") != 2)
1447 continue;
1448 if (strcasecmp(server, xchat_list_str(ph, list, "server")) != 0)
1449 continue;
1450 channel = xchat_list_str(ph, list, "channel");
1451 Tcl_DStringAppendElement(&ds, channel);
1453 xchat_list_free(ph, list);
1456 Tcl_AppendResult(irp, ds.string, NULL);
1458 Tcl_DStringFree(&ds);
1460 xchat_set_context(ph, origctx);
1462 return TCL_OK;
1465 static int tcl_servers(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
1467 const char *server;
1468 xchat_list *list;
1469 Tcl_DString ds;
1471 BADARGS(1, 1, "");
1473 Tcl_DStringInit(&ds);
1475 list = xchat_list_get(ph, "channels");
1477 if (list != NULL) {
1478 while (xchat_list_next(ph, list)) {
1479 if (xchat_list_int(ph, list, "type") == 1) {
1480 server = xchat_list_str(ph, list, "server");
1481 Tcl_DStringAppendElement(&ds, server);
1484 xchat_list_free(ph, list);
1487 Tcl_AppendResult(irp, ds.string, NULL);
1489 Tcl_DStringFree(&ds);
1491 return TCL_OK;
1494 static int tcl_queries(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
1496 const char *server, *channel;
1497 xchat_list *list;
1498 Tcl_DString ds;
1499 xchat_context *origctx;
1500 xchat_context *ctx;
1502 origctx = xchat_get_context(ph);
1504 BADARGS(1, 2, " ?server|network|context?");
1506 if (argc == 2) {
1507 ctx = xchat_smart_context(argv[1], NULL);
1508 CHECKCTX(ctx);
1509 xchat_set_context(ph, ctx);
1512 server = (char *) xchat_get_info(ph, "server");
1514 Tcl_DStringInit(&ds);
1516 list = xchat_list_get(ph, "channels");
1518 if (list != NULL) {
1519 while (xchat_list_next(ph, list)) {
1520 if (xchat_list_int(ph, list, "type") != 3)
1521 continue;
1522 if (strcasecmp(server, xchat_list_str(ph, list, "server")) != 0)
1523 continue;
1524 channel = xchat_list_str(ph, list, "channel");
1525 Tcl_DStringAppendElement(&ds, channel);
1527 xchat_list_free(ph, list);
1530 Tcl_AppendResult(irp, ds.string, NULL);
1532 Tcl_DStringFree(&ds);
1534 xchat_set_context(ph, origctx);
1536 return TCL_OK;
1539 static int tcl_users(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
1541 xchat_context *origctx, *ctx = NULL;
1542 xchat_list *list;
1543 Tcl_DString ds;
1545 BADARGS(1, 3, " ?server|network|context? ?channel?");
1547 origctx = xchat_get_context(ph);
1549 switch (argc) {
1550 case 1:
1551 ctx = origctx;
1552 break;
1553 case 2:
1554 ctx = xchat_smart_context(argv[1], NULL);
1555 break;
1556 case 3:
1557 ctx = xchat_smart_context(argv[1], argv[2]);
1558 break;
1559 default:;
1562 CHECKCTX(ctx);
1564 xchat_set_context(ph, ctx);
1566 Tcl_DStringInit(&ds);
1568 list = xchat_list_get(ph, "users");
1570 if (list != NULL) {
1572 Tcl_DStringStartSublist(&ds);
1573 Tcl_DStringAppendElement(&ds, "nick");
1574 Tcl_DStringAppendElement(&ds, "host");
1575 Tcl_DStringAppendElement(&ds, "prefix");
1576 Tcl_DStringAppendElement(&ds, "away");
1577 Tcl_DStringAppendElement(&ds, "lasttalk");
1578 Tcl_DStringAppendElement(&ds, "selected");
1579 Tcl_DStringEndSublist(&ds);
1581 while (xchat_list_next(ph, list)) {
1582 Tcl_DStringStartSublist(&ds);
1583 Tcl_DStringAppendElement(&ds, (const char *) xchat_list_str(ph, list, "nick"));
1584 Tcl_DStringAppendElement(&ds, (const char *) xchat_list_str(ph, list, "host"));
1585 Tcl_DStringAppendElement(&ds, (const char *) xchat_list_str(ph, list, "prefix"));
1586 Tcl_DStringAppendElement(&ds, myitoa((long)xchat_list_int(ph, list, "away")));
1587 Tcl_DStringAppendElement(&ds, myitoa((long)xchat_list_time(ph, list, "lasttalk")));
1588 Tcl_DStringAppendElement(&ds, myitoa((long)xchat_list_int(ph, list, "selected")));
1589 Tcl_DStringEndSublist(&ds);
1592 xchat_list_free(ph, list);
1595 Tcl_AppendResult(irp, ds.string, NULL);
1597 Tcl_DStringFree(&ds);
1599 xchat_set_context(ph, origctx);
1601 return TCL_OK;
1604 static int tcl_notifylist(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
1606 xchat_list *list;
1607 Tcl_DString ds;
1609 BADARGS(1, 1, "");
1611 Tcl_DStringInit(&ds);
1613 list = xchat_list_get(ph, "notify");
1615 if (list != NULL) {
1617 Tcl_DStringStartSublist(&ds);
1618 Tcl_DStringAppendElement(&ds, "nick");
1619 Tcl_DStringAppendElement(&ds, "flags");
1620 Tcl_DStringAppendElement(&ds, "on");
1621 Tcl_DStringAppendElement(&ds, "off");
1622 Tcl_DStringAppendElement(&ds, "seen");
1623 Tcl_DStringAppendElement(&ds, "networks");
1624 Tcl_DStringEndSublist(&ds);
1626 while (xchat_list_next(ph, list)) {
1627 Tcl_DStringStartSublist(&ds);
1628 Tcl_DStringAppendElement(&ds, (const char *) xchat_list_str(ph, list, "nick"));
1629 Tcl_DStringAppendElement(&ds, (const char *) xchat_list_str(ph, list, "flags"));
1630 Tcl_DStringAppendElement(&ds, myitoa((long)xchat_list_time(ph, list, "on")));
1631 Tcl_DStringAppendElement(&ds, myitoa((long)xchat_list_time(ph, list, "off")));
1632 Tcl_DStringAppendElement(&ds, myitoa((long)xchat_list_time(ph, list, "seen")));
1633 Tcl_DStringAppendElement(&ds, (const char *) xchat_list_str(ph, list, "networks"));
1634 Tcl_DStringEndSublist(&ds);
1637 xchat_list_free(ph, list);
1641 Tcl_AppendResult(irp, ds.string, NULL);
1643 Tcl_DStringFree(&ds);
1645 return TCL_OK;
1648 static int tcl_chats(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
1650 xchat_list *list;
1651 Tcl_DString ds;
1653 BADARGS(1, 1, "");
1655 Tcl_DStringInit(&ds);
1657 list = xchat_list_get(ph, "dcc");
1659 if (list != NULL) {
1660 while (xchat_list_next(ph, list)) {
1661 switch (xchat_list_int(ph, list, "type")) {
1662 case 2:
1663 case 3:
1664 if (xchat_list_int(ph, list, "status") == 1)
1665 Tcl_DStringAppendElement(&ds, (const char *) xchat_list_str(ph, list, "nick"));
1666 break;
1669 xchat_list_free(ph, list);
1672 Tcl_AppendResult(irp, ds.string, NULL);
1674 Tcl_DStringFree(&ds);
1676 return TCL_OK;
1679 static int tcl_ignores(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
1681 xchat_list *list;
1682 Tcl_DString ds;
1683 int flags;
1685 BADARGS(1, 1, "");
1687 Tcl_DStringInit(&ds);
1689 list = xchat_list_get(ph, "ignore");
1691 if (list != NULL) {
1693 while (xchat_list_next(ph, list)) {
1694 Tcl_DStringStartSublist(&ds);
1695 Tcl_DStringAppendElement(&ds, (const char *) xchat_list_str(ph, list, "mask"));
1696 Tcl_DStringStartSublist(&ds);
1697 flags = xchat_list_int(ph, list, "flags");
1698 if (flags & 1)
1699 Tcl_DStringAppendElement(&ds, "PRIVMSG");
1700 if (flags & 2)
1701 Tcl_DStringAppendElement(&ds, "NOTICE");
1702 if (flags & 4)
1703 Tcl_DStringAppendElement(&ds, "CHANNEL");
1704 if (flags & 8)
1705 Tcl_DStringAppendElement(&ds, "CTCP");
1706 if (flags & 16)
1707 Tcl_DStringAppendElement(&ds, "INVITE");
1708 if (flags & 32)
1709 Tcl_DStringAppendElement(&ds, "UNIGNORE");
1710 if (flags & 64)
1711 Tcl_DStringAppendElement(&ds, "NOSAVE");
1712 Tcl_DStringEndSublist(&ds);
1713 Tcl_DStringEndSublist(&ds);
1715 xchat_list_free(ph, list);
1718 Tcl_AppendResult(irp, ds.string, NULL);
1720 Tcl_DStringFree(&ds);
1722 return TCL_OK;
1725 static int tcl_dcclist(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
1727 xchat_list *list;
1728 Tcl_DString ds;
1729 int dcctype;
1731 BADARGS(1, 1, "");
1733 Tcl_DStringInit(&ds);
1735 list = xchat_list_get(ph, "dcc");
1737 if (list != NULL) {
1739 while (xchat_list_next(ph, list)) {
1740 Tcl_DStringStartSublist(&ds);
1741 dcctype = xchat_list_int(ph, list, "type");
1742 switch (dcctype) {
1743 case 0:
1744 Tcl_DStringAppendElement(&ds, "filesend");
1745 break;
1746 case 1:
1747 Tcl_DStringAppendElement(&ds, "filerecv");
1748 break;
1749 case 2:
1750 Tcl_DStringAppendElement(&ds, "chatrecv");
1751 break;
1752 case 3:
1753 Tcl_DStringAppendElement(&ds, "chatsend");
1754 break;
1756 switch (xchat_list_int(ph, list, "status")) {
1757 case 0:
1758 Tcl_DStringAppendElement(&ds, "queued");
1759 break;
1760 case 1:
1761 Tcl_DStringAppendElement(&ds, "active");
1762 break;
1763 case 2:
1764 Tcl_DStringAppendElement(&ds, "failed");
1765 break;
1766 case 3:
1767 Tcl_DStringAppendElement(&ds, "done");
1768 break;
1769 case 4:
1770 Tcl_DStringAppendElement(&ds, "connecting");
1771 break;
1772 case 5:
1773 Tcl_DStringAppendElement(&ds, "aborted");
1774 break;
1777 Tcl_DStringAppendElement(&ds, (const char *) xchat_list_str(ph, list, "nick"));
1779 switch (dcctype) {
1780 case 0:
1781 Tcl_DStringAppendElement(&ds, (const char *) xchat_list_str(ph, list, "file"));
1782 break;
1783 case 1:
1784 Tcl_DStringAppendElement(&ds, (const char *) xchat_list_str(ph, list, "destfile"));
1785 break;
1788 Tcl_DStringAppendElement(&ds, myitoa((long)xchat_list_int(ph, list, "size")));
1789 Tcl_DStringAppendElement(&ds, myitoa((long)xchat_list_int(ph, list, "resume")));
1790 Tcl_DStringAppendElement(&ds, myitoa((long)xchat_list_int(ph, list, "pos")));
1791 Tcl_DStringAppendElement(&ds, myitoa((long)xchat_list_int(ph, list, "cps")));
1792 Tcl_DStringAppendElement(&ds, myitoa((long)xchat_list_int(ph, list, "address32")));
1793 Tcl_DStringAppendElement(&ds, myitoa((long)xchat_list_int(ph, list, "port")));
1794 Tcl_DStringEndSublist(&ds);
1796 xchat_list_free(ph, list);
1799 Tcl_AppendResult(irp, ds.string, NULL);
1801 Tcl_DStringFree(&ds);
1803 return TCL_OK;
1807 static int tcl_strip(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
1809 char *new_text;
1810 int flags = 1 | 2;
1812 BADARGS(2, 3, " text ?flags?");
1814 if (argc == 3) {
1815 if (Tcl_GetInt(irp, argv[2], &flags) != TCL_OK)
1816 return TCL_ERROR;
1819 new_text = xchat_strip(ph, argv[1], -1, flags);
1821 if(new_text) {
1822 Tcl_AppendResult(irp, new_text, NULL);
1823 xchat_free(ph, new_text);
1826 return TCL_OK;
1829 static int tcl_topic(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
1831 xchat_context *origctx, *ctx = NULL;
1832 BADARGS(1, 3, " ?server|network|context? ?channel?");
1834 origctx = xchat_get_context(ph);
1836 switch (argc) {
1837 case 1:
1838 ctx = origctx;
1839 break;
1840 case 2:
1841 ctx = xchat_smart_context(argv[1], NULL);
1842 break;
1843 case 3:
1844 ctx = xchat_smart_context(argv[1], argv[2]);
1845 break;
1846 default:;
1849 CHECKCTX(ctx);
1851 xchat_set_context(ph, ctx);
1852 Tcl_AppendResult(irp, xchat_get_info(ph, "topic"), NULL);
1853 xchat_set_context(ph, origctx);
1855 return TCL_OK;
1858 static int tcl_xchat_nickcmp(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
1860 BADARGS(3, 3, " string1 string2");
1861 Tcl_AppendResult(irp, myitoa((long)xchat_nickcmp(ph, argv[1], argv[2])), NULL);
1862 return TCL_OK;
1865 static int tcl_word(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
1867 int index;
1869 BADARGS(2, 2, " index");
1871 if (Tcl_GetInt(irp, argv[1], &index) != TCL_OK)
1872 return TCL_ERROR;
1874 if (!index || (index > 31))
1875 Tcl_AppendResult(interp, "", NULL);
1876 else
1877 Tcl_AppendResult(interp, complete[complete_level].word[index], NULL);
1879 return TCL_OK;
1882 static int tcl_word_eol(ClientData cd, Tcl_Interp * irp, int argc, const char *argv[])
1884 int index;
1886 BADARGS(2, 2, " index");
1888 if (Tcl_GetInt(irp, argv[1], &index) != TCL_OK)
1889 return TCL_ERROR;
1891 if (!index || (index > 31))
1892 Tcl_AppendResult(interp, "", NULL);
1893 else
1894 Tcl_AppendResult(interp, complete[complete_level].word_eol[index], NULL);
1896 return TCL_OK;
1899 static int Command_Alias(char *word[], char *word_eol[], void *userdata)
1901 alias *aliasPtr;
1902 Tcl_HashEntry *entry;
1903 xchat_context *origctx;
1904 int dummy;
1905 char *string;
1907 if (complete_level == MAX_COMPLETES)
1908 return XCHAT_EAT_NONE;
1910 complete_level++;
1911 complete[complete_level].defresult = XCHAT_EAT_ALL;
1912 complete[complete_level].result = XCHAT_EAT_NONE;
1913 complete[complete_level].word = word;
1914 complete[complete_level].word_eol = word_eol;
1916 string = StrDup(word[1], &dummy);
1918 Tcl_UtfToUpper(string);
1920 if ((entry = Tcl_FindHashEntry(&aliasTablePtr, string)) != NULL) {
1921 aliasPtr = Tcl_GetHashValue(entry);
1922 origctx = xchat_get_context(ph);
1923 if (EvalInternalProc(aliasPtr->procPtr, 2, string, word_eol[2]) == TCL_ERROR) {
1924 xchat_printf(ph, "\0039Tcl plugin\003\tERROR (alias %s) ", string);
1925 NiceErrorInfo ();
1927 xchat_set_context(ph, origctx);
1930 Tcl_Free(string);
1932 return complete[complete_level--].result;
1935 static int Null_Command_Alias(char *word[], char *word_eol[], void *userdata)
1937 alias *aliasPtr;
1938 Tcl_HashEntry *entry;
1939 xchat_context *origctx;
1940 int dummy;
1941 const char *channel;
1942 char *string;
1943 Tcl_DString ds;
1944 static int recurse = 0;
1946 if (recurse)
1947 return XCHAT_EAT_NONE;
1949 if (complete_level == MAX_COMPLETES)
1950 return XCHAT_EAT_NONE;
1952 complete_level++;
1953 complete[complete_level].defresult = XCHAT_EAT_ALL;
1954 complete[complete_level].result = XCHAT_EAT_NONE;
1955 complete[complete_level].word = word;
1956 complete[complete_level].word_eol = word_eol;
1958 recurse++;
1960 channel = xchat_get_info(ph, "channel");
1961 Tcl_DStringInit(&ds);
1962 Tcl_DStringAppend(&ds, "@", 1);
1963 Tcl_DStringAppend(&ds, channel, strlen(channel));
1964 string = StrDup(ds.string, &dummy);
1965 Tcl_DStringFree(&ds);
1967 Tcl_UtfToUpper(string);
1969 if ((entry = Tcl_FindHashEntry(&aliasTablePtr, string)) != NULL) {
1970 aliasPtr = Tcl_GetHashValue(entry);
1971 origctx = xchat_get_context(ph);
1972 if (EvalInternalProc(aliasPtr->procPtr, 2, string, word_eol[1]) == TCL_ERROR) {
1973 xchat_printf(ph, "\0039Tcl plugin\003\tERROR (alias %s) ", string);
1974 NiceErrorInfo ();
1976 xchat_set_context(ph, origctx);
1979 Tcl_Free(string);
1981 recurse--;
1983 return complete[complete_level--].result;
1986 static int Command_TCL(char *word[], char *word_eol[], void *userdata)
1988 const char *errorInfo;
1990 complete_level++;
1991 complete[complete_level].word = word;
1992 complete[complete_level].word_eol = word_eol;
1994 if (Tcl_Eval(interp, word_eol[2]) == TCL_ERROR) {
1995 errorInfo = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
1996 xchat_printf(ph, "\0039Tcl plugin\003\tERROR: %s ", errorInfo);
1997 } else
1998 xchat_printf(ph, "\0039Tcl plugin\003\tRESULT: %s ", Tcl_GetStringResult(interp));
2000 complete_level--;
2002 return XCHAT_EAT_ALL;
2005 static int Command_Source(char *word[], char *word_eol[], void *userdata)
2007 const char *xchatdir;
2008 Tcl_DString ds;
2009 struct stat dummy;
2010 int len;
2011 const char *errorInfo;
2013 if (!strlen(word_eol[2]))
2014 return XCHAT_EAT_NONE;
2016 complete_level++;
2017 complete[complete_level].word = word;
2018 complete[complete_level].word_eol = word_eol;
2020 len = strlen(word[2]);
2022 if (len > 4 && strcasecmp(".tcl", word[2] + len - 4) == 0) {
2024 xchatdir = xchat_get_info(ph, "xchatdir");
2026 Tcl_DStringInit(&ds);
2028 if (stat(word_eol[2], &dummy) == 0) {
2029 Tcl_DStringAppend(&ds, word_eol[2], strlen(word_eol[2]));
2030 } else {
2031 if (!strchr(word_eol[2], '/')) {
2032 Tcl_DStringAppend(&ds, xchatdir, strlen(xchatdir));
2033 Tcl_DStringAppend(&ds, "/", 1);
2034 Tcl_DStringAppend(&ds, word_eol[2], strlen(word_eol[2]));
2038 if (Tcl_EvalFile(interp, ds.string) == TCL_ERROR) {
2039 errorInfo = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
2040 xchat_printf(ph, "\0039Tcl plugin\003\tERROR: %s ", errorInfo);
2041 } else
2042 xchat_printf(ph, "\0039Tcl plugin\003\tSourced %s\n", ds.string);
2044 Tcl_DStringFree(&ds);
2046 complete_level--;
2048 return XCHAT_EAT_XCHAT;
2050 } else {
2051 complete_level--;
2052 return XCHAT_EAT_NONE;
2057 static int Command_Reloadall(char *word[], char *word_eol[], void *userdata)
2059 Tcl_Plugin_DeInit();
2060 Tcl_Plugin_Init();
2062 xchat_print(ph, "\0039Tcl plugin\003\tRehashed\n");
2064 return XCHAT_EAT_ALL;
2067 static int TCL_Event_Handler(void *userdata)
2069 Tcl_DoOneEvent(TCL_DONT_WAIT);
2070 do_timer();
2071 return 1;
2074 static void Tcl_Plugin_Init()
2076 int x;
2077 const char *xchatdir;
2079 interp = Tcl_CreateInterp();
2081 Tcl_FindExecutable(NULL);
2083 Tcl_Init(interp);
2085 nextprocid = 0x1000;
2087 Tcl_CreateCommand(interp, "alias", tcl_alias, NULL, NULL);
2088 Tcl_CreateCommand(interp, "channels", tcl_channels, NULL, NULL);
2089 Tcl_CreateCommand(interp, "chats", tcl_chats, NULL, NULL);
2090 Tcl_CreateCommand(interp, "command", tcl_command, NULL, NULL);
2091 Tcl_CreateCommand(interp, "complete", tcl_complete, NULL, NULL);
2092 Tcl_CreateCommand(interp, "dcclist", tcl_dcclist, NULL, NULL);
2093 Tcl_CreateCommand(interp, "notifylist", tcl_notifylist, NULL, NULL);
2094 Tcl_CreateCommand(interp, "findcontext", tcl_findcontext, NULL, NULL);
2095 Tcl_CreateCommand(interp, "getcontext", tcl_getcontext, NULL, NULL);
2096 Tcl_CreateCommand(interp, "getinfo", tcl_getinfo, NULL, NULL);
2097 Tcl_CreateCommand(interp, "getlist", tcl_getlist, NULL, NULL);
2098 Tcl_CreateCommand(interp, "ignores", tcl_ignores, NULL, NULL);
2099 Tcl_CreateCommand(interp, "killtimer", tcl_killtimer, NULL, NULL);
2100 Tcl_CreateCommand(interp, "me", tcl_me, NULL, NULL);
2101 Tcl_CreateCommand(interp, "on", tcl_on, NULL, NULL);
2102 Tcl_CreateCommand(interp, "off", tcl_off, NULL, NULL);
2103 Tcl_CreateCommand(interp, "nickcmp", tcl_xchat_nickcmp, NULL, NULL);
2104 Tcl_CreateCommand(interp, "print", tcl_print, NULL, NULL);
2105 Tcl_CreateCommand(interp, "prefs", tcl_prefs, NULL, NULL);
2106 Tcl_CreateCommand(interp, "::puts", tcl_xchat_puts, NULL, NULL);
2107 Tcl_CreateCommand(interp, "queries", tcl_queries, NULL, NULL);
2108 Tcl_CreateCommand(interp, "raw", tcl_raw, NULL, NULL);
2109 Tcl_CreateCommand(interp, "servers", tcl_servers, NULL, NULL);
2110 Tcl_CreateCommand(interp, "setcontext", tcl_setcontext, NULL, NULL);
2111 Tcl_CreateCommand(interp, "strip", tcl_strip, NULL, NULL);
2112 Tcl_CreateCommand(interp, "timer", tcl_timer, NULL, NULL);
2113 Tcl_CreateCommand(interp, "timerexists", tcl_timerexists, NULL, NULL);
2114 Tcl_CreateCommand(interp, "timers", tcl_timers, NULL, NULL);
2115 Tcl_CreateCommand(interp, "topic", tcl_topic, NULL, NULL);
2116 Tcl_CreateCommand(interp, "users", tcl_users, NULL, NULL);
2117 Tcl_CreateCommand(interp, "word", tcl_word, NULL, NULL);
2118 Tcl_CreateCommand(interp, "word_eol", tcl_word_eol, NULL, NULL);
2120 Tcl_InitHashTable(&cmdTablePtr, TCL_STRING_KEYS);
2121 Tcl_InitHashTable(&aliasTablePtr, TCL_STRING_KEYS);
2123 bzero(timers, sizeof(timers));
2124 nexttimerid = 0;
2125 nexttimerindex = 0;
2127 for (x = 0; x < XC_SIZE; x++)
2128 xc[x].hook = NULL;
2130 xchatdir = xchat_get_info(ph, "xchatdir");
2132 if (Tcl_Eval(interp, unknown) == TCL_ERROR) {
2133 xchat_printf(ph, "Error sourcing internal 'unknown' (%s)\n", Tcl_GetStringResult(interp));
2136 if (Tcl_Eval(interp, inlinetcl) == TCL_ERROR) {
2137 xchat_printf(ph, "Error sourcing internal 'inlinetcl' (%s)\n", Tcl_GetStringResult(interp));
2140 if (Tcl_Eval(interp, sourcedirs) == TCL_ERROR) {
2141 xchat_printf(ph, "Error sourcing internal 'sourcedirs' (%s)\n", Tcl_GetStringResult(interp));
2146 static void Tcl_Plugin_DeInit()
2148 int x;
2149 char *procPtr;
2150 alias *aliasPtr;
2151 Tcl_HashEntry *entry;
2152 Tcl_HashSearch search;
2154 /* Be sure to free all the memory for ON and ALIAS entries */
2156 entry = Tcl_FirstHashEntry(&cmdTablePtr, &search);
2157 while (entry != NULL) {
2158 procPtr = Tcl_GetHashValue(entry);
2159 Tcl_Free(procPtr);
2160 entry = Tcl_NextHashEntry(&search);
2163 Tcl_DeleteHashTable(&cmdTablePtr);
2165 entry = Tcl_FirstHashEntry(&aliasTablePtr, &search);
2166 while (entry != NULL) {
2167 aliasPtr = Tcl_GetHashValue(entry);
2168 Tcl_Free(aliasPtr->procPtr);
2169 if (aliasPtr->hook)
2170 xchat_unhook(ph, aliasPtr->hook);
2171 Tcl_Free((char *) aliasPtr);
2172 entry = Tcl_NextHashEntry(&search);
2175 Tcl_DeleteHashTable(&aliasTablePtr);
2177 for (x = 1; x < MAX_TIMERS; x++) {
2178 if (timers[x].timerid) {
2179 timers[x].timerid = 0;
2180 if (timers[x].procPtr != NULL)
2181 Tcl_Free(timers[x].procPtr);
2182 timers[x].procPtr = NULL;
2183 break;
2187 for (x = 0; x < XC_SIZE; x++) {
2188 if (xc[x].hook != NULL) {
2189 xchat_unhook(ph, xc[x].hook);
2190 xc[x].hook = NULL;
2194 Tcl_DeleteInterp(interp);
2197 int xchat_plugin_init(xchat_plugin * plugin_handle, char **plugin_name, char **plugin_desc, char **plugin_version, char *arg)
2199 strncpy(VERSION, &RCSID[19], 5);
2201 ph = plugin_handle;
2203 if (initialized != 0) {
2204 xchat_print(ph, "Tcl plugin already loaded");
2205 reinit_tried++;
2206 return 0;
2208 initialized = 1;
2210 *plugin_name = "Tcl";
2211 *plugin_desc = "Tcl scripting interface";
2212 *plugin_version = VERSION;
2214 Tcl_Plugin_Init();
2216 raw_line_hook = xchat_hook_server(ph, "RAW LINE", XCHAT_PRI_NORM, Server_raw_line, NULL);
2217 Command_TCL_hook = xchat_hook_command(ph, "tcl", XCHAT_PRI_NORM, Command_TCL, 0, 0);
2218 Command_Source_hook = xchat_hook_command(ph, "source", XCHAT_PRI_NORM, Command_Source, 0, 0);
2219 Command_Reload_hook = xchat_hook_command(ph, "reloadall", XCHAT_PRI_NORM, Command_Reloadall, 0, 0);
2220 Command_Load_hook = xchat_hook_command(ph, "LOAD", XCHAT_PRI_NORM, Command_Source, 0, 0);
2221 Event_Handler_hook = xchat_hook_timer(ph, 100, TCL_Event_Handler, 0);
2222 Null_Command_hook = xchat_hook_command(ph, "", XCHAT_PRI_NORM, Null_Command_Alias, "", 0);
2224 xchat_print(ph, "Tcl interface loaded\n");
2226 return 1; /* return 1 for success */
2229 int xchat_plugin_deinit()
2231 if (reinit_tried) {
2232 reinit_tried--;
2233 return 1;
2236 xchat_unhook(ph, raw_line_hook);
2237 xchat_unhook(ph, Command_TCL_hook);
2238 xchat_unhook(ph, Command_Source_hook);
2239 xchat_unhook(ph, Command_Reload_hook);
2240 xchat_unhook(ph, Command_Load_hook);
2241 xchat_unhook(ph, Event_Handler_hook);
2242 xchat_unhook(ph, Null_Command_hook);
2244 Tcl_Plugin_DeInit();
2246 xchat_print(ph, "Tcl interface unloaded\n");
2247 initialized = 0;
2249 return 1;
2252 void xchat_plugin_get_info(char **name, char **desc, char **version, void **reserved)
2254 strncpy(VERSION, &RCSID[19], 5);
2255 *name = "tclplugin";
2256 *desc = "Tcl plugin for XChat";
2257 *version = VERSION;
2258 if (reserved)
2259 *reserved = NULL;