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 /***************************************************************************
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. *
16 ***************************************************************************/
18 static char RCSID
[] = "$Id: tclplugin.c,v 1.65 2012/07/26 20:02:12 mooooooo Exp $";
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"
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"
75 " return -code $code $msg\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"
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"
100 "puts \"Using 'exit' is bad\"\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"
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"
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
);
152 static int SourceInternalProc(int id
, const char *args
, const char *source
)
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
);
173 static int EvalInternalProc(const char *procname
, int ct
, ...)
180 Tcl_DStringInit(&ds
);
182 Tcl_MyDStringAppend(&ds
, (char *) procname
);
187 if ((buf
= va_arg(ap
, char *)) != NULL
)
188 Tcl_DStringAppendElement(&ds
, buf
);
190 Tcl_MyDStringAppend(&ds
, " \"\"");
195 result
= Tcl_Eval(interp
, ds
.string
);
197 Tcl_DStringFree(&ds
);
203 static void DeleteInternalProc(const char *proc
)
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
)
222 *length
= strlen(string
);
223 result
= Tcl_Alloc(*length
+ 1);
224 strncpy(result
, string
, *length
);
230 static char *myitoa(long value
)
232 static char result
[32];
233 sprintf(result
, "%ld", value
);
237 static xchat_context
*atoctx(const char *nptr
)
245 while (isnum
&& nptr
[x
]) {
246 if (!isdigit(nptr
[x
++]))
251 return (xchat_context
*) atol(nptr
);
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
;
265 return xchat_get_context(ph
);;
268 result
= xchat_find_context(ph
, arg1
, arg2
);
270 result
= xchat_find_context(ph
, arg2
, arg1
);
276 server
= xchat_get_info(ph
, "server");
278 list
= xchat_list_get(ph
, "channels");
282 while (xchat_list_next(ph
, list
)) {
284 ctx
= (xchat_context
*)xchat_list_str(ph
, list
, "context");
293 s
= xchat_list_str(ph
, list
, "server");
295 if (xchat_list_int(ph
, list
, "type") == 1) {
296 if (strcasecmp(arg1
, s
) == 0) {
300 n
= xchat_list_str(ph
, list
, "network");
302 if (strcasecmp(arg1
, n
) == 0) {
308 if ((strcasecmp(server
, s
) == 0) && (strcasecmp(arg1
, xchat_list_str(ph
, list
, "channel")) == 0)) {
316 xchat_list_free(ph
, list
);
324 static void queue_nexttimer()
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
;
342 static int insert_timer(int seconds
, int count
, const char *script
)
352 id
= (nextprocid
++ % INT_MAX
) + 1;
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
);
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
);
369 return (timers
[x
].timerid
);
376 static void do_timer()
378 xchat_context
*origctx
;
387 if (now
< timers
[nexttimerindex
].timestamp
)
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
);
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
;
409 timers
[index
].timestamp
+= timers
[index
].seconds
;
418 static int Server_raw_line(char *word
[], char *word_eol
[], void *userdata
)
420 char *src
, *cmd
, *dest
, *rest
;
421 char *chancmd
= NULL
;
423 Tcl_HashEntry
*entry
= NULL
;
424 xchat_context
*origctx
;
430 int list_argc
, proc_argc
;
431 const char **list_argv
, **proc_argv
;
435 return XCHAT_EAT_NONE
;
437 if (complete_level
== MAX_COMPLETES
)
438 return XCHAT_EAT_NONE
;
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] == ':') {
454 if (word_eol
[2][0] == ':') {
470 if (rest
[0] == 0x01) {
472 if (strcasecmp("PRIVMSG", cmd
) == 0) {
473 if (strncasecmp(rest
, "ACTION ", 7) == 0) {
478 } else if (!strcasecmp("NOTICE", cmd
))
481 } else if (!strcasecmp("NOTICE", cmd
) && (strchr(src
, '!') == NULL
)) {
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
) {
499 string
= StrDup(cmd
, &dummy
);
500 Tcl_UtfToUpper(string
);
501 entry
= Tcl_FindHashEntry(&cmdTablePtr
, string
);
506 procList
= Tcl_GetHashValue(entry
);
508 if (isalpha(dest
[0]))
511 rest
= StrDup(rest
, &len
);
514 if ((len
> 1) && (rest
[len
- 1] == 0x01))
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
)
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]);
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
))
540 Tcl_Free((char *) list_argv
);
551 return complete
[complete_level
--].result
;
555 static int Print_Hook(char *word
[], void *userdata
)
558 Tcl_HashEntry
*entry
;
559 xchat_context
*origctx
;
561 int list_argc
, proc_argc
;
562 const char **list_argv
, **proc_argv
;
566 if (complete_level
== MAX_COMPLETES
)
567 return XCHAT_EAT_NONE
;
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
)
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]);
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]);
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
))
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
[])
634 BADARGS(2, 2, " schedid");
636 if (Tcl_GetInt(irp
, argv
[1], &timerid
) != TCL_OK
) {
637 Tcl_AppendResult(irp
, "Invalid timer id", NULL
);
642 for (x
= 1; x
< MAX_TIMERS
; x
++) {
643 if (timers
[x
].timerid
== timerid
) {
644 Tcl_AppendResult(irp
, "1", NULL
);
650 Tcl_AppendResult(irp
, "0", NULL
);
655 static int tcl_killtimer(ClientData cd
, Tcl_Interp
* irp
, int argc
, const char *argv
[])
660 BADARGS(2, 2, " timerid");
662 if (Tcl_GetInt(irp
, argv
[1], &timerid
) != TCL_OK
) {
663 Tcl_AppendResult(irp
, "Invalid timer id", NULL
);
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
;
686 static int tcl_timers(ClientData cd
, Tcl_Interp
* irp
, int argc
, const char *argv
[])
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
);
716 static int tcl_timer(ClientData cd
, Tcl_Interp
* irp
, int argc
, const char *argv
[])
725 BADARGS(3, 6, " ?-repeat? ?-count times? seconds {script | procname ?args?}");
728 if (strcasecmp(argv
[first
], "-repeat") == 0) {
730 } else if (strcasecmp(argv
[first
], "-count") == 0) {
731 if (Tcl_GetInt(irp
, argv
[++first
], &count
) != TCL_OK
)
739 if (repeat
&& !count
)
745 if (Tcl_GetInt(irp
, argv
[first
++], &seconds
) != TCL_OK
)
748 if ((timerid
= insert_timer(seconds
, count
, argv
[first
++])) == -1) {
749 Tcl_AppendResult(irp
, "0", NULL
);
753 sprintf(reply
, "%d", timerid
);
755 Tcl_AppendResult(irp
, reply
, NULL
);
762 static int tcl_on(ClientData cd
, Tcl_Interp
* irp
, int argc
, const char *argv
[])
766 Tcl_HashEntry
*entry
;
772 int list_argc
, proc_argc
;
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]);
786 token
= StrDup(argv
[1], &dummy
);
787 Tcl_UtfToUpper(token
);
789 Tcl_DStringInit(&ds
);
791 entry
= Tcl_CreateHashEntry(&cmdTablePtr
, token
, &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
)
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
);
804 DeleteInternalProc(proc_argv
[1]);
806 Tcl_Free((char *) proc_argv
);
808 Tcl_Free((char *) list_argv
);
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
);
834 Tcl_DStringFree(&ds
);
839 static int tcl_off(ClientData cd
, Tcl_Interp
* irp
, int argc
, const char *argv
[])
842 Tcl_HashEntry
*entry
;
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
);
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
)
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
);
873 DeleteInternalProc(proc_argv
[1]);
874 Tcl_Free((char *) proc_argv
);
876 Tcl_Free((char *) list_argv
);
883 procList
= StrDup(ds
.string
, &dummy
);
884 Tcl_SetHashValue(entry
, procList
);
886 Tcl_DeleteHashEntry(entry
);
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
;
905 Tcl_DStringFree(&ds
);
910 static int tcl_alias(ClientData cd
, Tcl_Interp
* irp
, int argc
, const char *argv
[])
914 Tcl_HashEntry
*entry
;
916 const char *help
= NULL
;
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])) {
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]);
938 entry
= Tcl_CreateHashEntry(&aliasTablePtr
, string
, &newentry
);
940 aliasPtr
= (alias
*) Tcl_Alloc(sizeof(alias
));
941 if (string
[0] == '@')
942 aliasPtr
->hook
= NULL
;
944 aliasPtr
->hook
= xchat_hook_command(ph
, string
, XCHAT_PRI_NORM
, Command_Alias
, help
, 0);
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
);
957 if ((entry
= Tcl_FindHashEntry(&aliasTablePtr
, string
)) != NULL
) {
958 aliasPtr
= Tcl_GetHashValue(entry
);
959 DeleteInternalProc(aliasPtr
->procPtr
);
960 Tcl_Free(aliasPtr
->procPtr
);
962 xchat_unhook(ph
, aliasPtr
->hook
);
963 Tcl_Free((char *) aliasPtr
);
964 Tcl_DeleteHashEntry(entry
);
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?");
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
;
988 BADARGS(1, 2, " ?EAT_NONE|EAT_XCHAT|EAT_PLUGIN|EAT_ALL?");
991 complete
[complete_level
].result
= complete
[complete_level
].defresult
;
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
);
1011 ctx
= xchat_smart_context(argv
[1], NULL
);
1014 ctx
= xchat_smart_context(argv
[1], argv
[2]);
1021 string
= argv
[argc
- 1];
1023 if (string
[0] == '/')
1026 xchat_set_context(ph
, ctx
);
1027 xchat_command(ph
, string
);
1028 xchat_set_context(ph
, origctx
);
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
);
1048 ctx
= xchat_smart_context(argv
[1], NULL
);
1051 ctx
= xchat_smart_context(argv
[1], argv
[2]);
1058 string
= argv
[argc
- 1];
1060 xchat_set_context(ph
, ctx
);
1061 xchat_commandf(ph
, "RAW %s", string
);
1062 xchat_set_context(ph
, origctx
);
1068 static int tcl_prefs(ClientData cd
, Tcl_Interp
* irp
, int argc
, const char *argv
[])
1073 BADARGS(2, 2, " name");
1075 switch (xchat_get_prefs (ph
, argv
[1], &str
, &i
)) {
1077 Tcl_AppendResult(irp
, str
, NULL
);
1081 Tcl_AppendResult(irp
, myitoa(i
), NULL
);
1084 Tcl_AppendResult(irp
, NULL
);
1090 static int tcl_info(ClientData cd
, Tcl_Interp
* irp
, int argc
, const char *argv
[], char *id
)
1094 xchat_context
*origctx
, *ctx
;
1097 BADARGS(2, 3, " ?server|network|context? id");
1100 BADARGS(1, 2, " ?server|network|context?");
1104 origctx
= xchat_get_context(ph
);
1106 if (argc
== max_argc
) {
1107 ctx
= xchat_smart_context(argv
[1], NULL
);
1109 xchat_set_context(ph
, ctx
);
1113 id
= (char *) argv
[argc
-1];
1115 if ((result
= (char *) xchat_get_info(ph
, id
)) == NULL
)
1118 Tcl_AppendResult(irp
, result
, NULL
);
1120 xchat_set_context(ph
, origctx
);
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
;
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");
1158 for (i
= 0; fields
[i
] != NULL
; i
++) {
1159 Tcl_DStringAppendElement(&ds
, fields
[i
]);
1164 for (i
= 0; fields
[i
] != NULL
; i
++) {
1165 if (strcmp(fields
[i
], argv
[1]) == 0) {
1174 list
= xchat_list_get(ph
, name
);
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]) {
1197 sattr
= xchat_list_str(ph
, list
, (char *) field
);
1198 Tcl_DStringAppendElement(&ds
, sattr
);
1201 iattr
= xchat_list_int(ph
, list
, (char *) field
);
1202 Tcl_DStringAppendElement(&ds
, myitoa((long)iattr
));
1205 t
= xchat_list_time(ph
, list
, (char *) field
);
1206 Tcl_DStringAppendElement(&ds
, myitoa((long)t
));
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
));
1214 Tcl_DStringAppendElement(&ds
, "*");
1217 Tcl_DStringAppendElement(&ds
, "*");
1222 Tcl_DStringEndSublist(&ds
);
1226 xchat_list_free(ph
, list
);
1230 xchat_set_context(ph
, origctx
);
1232 Tcl_AppendResult(irp
, ds
.string
, NULL
);
1234 Tcl_DStringFree(&ds
);
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
[])
1250 const char *channelId
= NULL
;
1253 int trap_stdout
= 0;
1264 if (strcmp(argv
[1], "-nonewline") == 0) {
1269 channelId
= argv
[1];
1275 if (strcmp(argv
[1], "-nonewline") == 0) {
1276 channelId
= argv
[2];
1279 if (strcmp(argv
[3], "nonewline") != 0) {
1280 Tcl_AppendResult(interp
, "bad argument \"", argv
[3], "\": should be \"nonewline\"", (char *) NULL
);
1283 channelId
= argv
[1];
1290 Tcl_AppendResult(interp
, argv
, "?-nonewline? ?channelId? string", NULL
);
1294 if (!trap_stdout
&& (strcmp(channelId
, "stdout") == 0))
1299 xchat_printf(ph
, "%s\n", string
);
1301 xchat_print(ph
, string
);
1305 chan
= Tcl_GetChannel(interp
, channelId
, &mode
);
1306 if (chan
== (Tcl_Channel
) NULL
) {
1309 if ((mode
& TCL_WRITABLE
) == 0) {
1310 Tcl_AppendResult(interp
, "channel \"", channelId
, "\" wasn't opened for writing", (char *) NULL
);
1314 result
= Tcl_Write(chan
, string
, strlen(string
));
1319 result
= Tcl_WriteChars(chan
, "\n", 1);
1327 Tcl_AppendResult(interp
, "error writing \"", channelId
, "\": ", Tcl_PosixError(interp
), (char *) NULL
);
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
);
1347 ctx
= xchat_smart_context(argv
[1], NULL
);
1350 ctx
= xchat_smart_context(argv
[1], argv
[2]);
1357 string
= argv
[argc
- 1];
1359 xchat_set_context(ph
, ctx
);
1360 xchat_print(ph
, string
);
1361 xchat_set_context(ph
, origctx
);
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
);
1376 xchat_set_context(ph
, ctx
);
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?");
1389 ctx
= xchat_find_context(ph
, NULL
, NULL
);
1392 ctx
= xchat_smart_context(argv
[1], NULL
);
1395 ctx
= xchat_smart_context(argv
[1], argv
[2]);
1402 Tcl_AppendResult(irp
, myitoa((long)ctx
), NULL
);
1407 static int tcl_getcontext(ClientData cd
, Tcl_Interp
* irp
, int argc
, const char *argv
[])
1409 xchat_context
*ctx
= NULL
;
1413 ctx
= xchat_get_context(ph
);
1415 Tcl_AppendResult(irp
, myitoa((long)ctx
), NULL
);
1420 static int tcl_channels(ClientData cd
, Tcl_Interp
* irp
, int argc
, const char *argv
[])
1422 const char *server
, *channel
;
1425 xchat_context
*origctx
;
1428 origctx
= xchat_get_context(ph
);
1430 BADARGS(1, 2, " ?server|network|context?");
1433 ctx
= xchat_smart_context(argv
[1], NULL
);
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");
1445 while (xchat_list_next(ph
, list
)) {
1446 if (xchat_list_int(ph
, list
, "type") != 2)
1448 if (strcasecmp(server
, xchat_list_str(ph
, list
, "server")) != 0)
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
);
1465 static int tcl_servers(ClientData cd
, Tcl_Interp
* irp
, int argc
, const char *argv
[])
1473 Tcl_DStringInit(&ds
);
1475 list
= xchat_list_get(ph
, "channels");
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
);
1494 static int tcl_queries(ClientData cd
, Tcl_Interp
* irp
, int argc
, const char *argv
[])
1496 const char *server
, *channel
;
1499 xchat_context
*origctx
;
1502 origctx
= xchat_get_context(ph
);
1504 BADARGS(1, 2, " ?server|network|context?");
1507 ctx
= xchat_smart_context(argv
[1], NULL
);
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");
1519 while (xchat_list_next(ph
, list
)) {
1520 if (xchat_list_int(ph
, list
, "type") != 3)
1522 if (strcasecmp(server
, xchat_list_str(ph
, list
, "server")) != 0)
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
);
1539 static int tcl_users(ClientData cd
, Tcl_Interp
* irp
, int argc
, const char *argv
[])
1541 xchat_context
*origctx
, *ctx
= NULL
;
1545 BADARGS(1, 3, " ?server|network|context? ?channel?");
1547 origctx
= xchat_get_context(ph
);
1554 ctx
= xchat_smart_context(argv
[1], NULL
);
1557 ctx
= xchat_smart_context(argv
[1], argv
[2]);
1564 xchat_set_context(ph
, ctx
);
1566 Tcl_DStringInit(&ds
);
1568 list
= xchat_list_get(ph
, "users");
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
);
1604 static int tcl_notifylist(ClientData cd
, Tcl_Interp
* irp
, int argc
, const char *argv
[])
1611 Tcl_DStringInit(&ds
);
1613 list
= xchat_list_get(ph
, "notify");
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
);
1648 static int tcl_chats(ClientData cd
, Tcl_Interp
* irp
, int argc
, const char *argv
[])
1655 Tcl_DStringInit(&ds
);
1657 list
= xchat_list_get(ph
, "dcc");
1660 while (xchat_list_next(ph
, list
)) {
1661 switch (xchat_list_int(ph
, list
, "type")) {
1664 if (xchat_list_int(ph
, list
, "status") == 1)
1665 Tcl_DStringAppendElement(&ds
, (const char *) xchat_list_str(ph
, list
, "nick"));
1669 xchat_list_free(ph
, list
);
1672 Tcl_AppendResult(irp
, ds
.string
, NULL
);
1674 Tcl_DStringFree(&ds
);
1679 static int tcl_ignores(ClientData cd
, Tcl_Interp
* irp
, int argc
, const char *argv
[])
1687 Tcl_DStringInit(&ds
);
1689 list
= xchat_list_get(ph
, "ignore");
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");
1699 Tcl_DStringAppendElement(&ds
, "PRIVMSG");
1701 Tcl_DStringAppendElement(&ds
, "NOTICE");
1703 Tcl_DStringAppendElement(&ds
, "CHANNEL");
1705 Tcl_DStringAppendElement(&ds
, "CTCP");
1707 Tcl_DStringAppendElement(&ds
, "INVITE");
1709 Tcl_DStringAppendElement(&ds
, "UNIGNORE");
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
);
1725 static int tcl_dcclist(ClientData cd
, Tcl_Interp
* irp
, int argc
, const char *argv
[])
1733 Tcl_DStringInit(&ds
);
1735 list
= xchat_list_get(ph
, "dcc");
1739 while (xchat_list_next(ph
, list
)) {
1740 Tcl_DStringStartSublist(&ds
);
1741 dcctype
= xchat_list_int(ph
, list
, "type");
1744 Tcl_DStringAppendElement(&ds
, "filesend");
1747 Tcl_DStringAppendElement(&ds
, "filerecv");
1750 Tcl_DStringAppendElement(&ds
, "chatrecv");
1753 Tcl_DStringAppendElement(&ds
, "chatsend");
1756 switch (xchat_list_int(ph
, list
, "status")) {
1758 Tcl_DStringAppendElement(&ds
, "queued");
1761 Tcl_DStringAppendElement(&ds
, "active");
1764 Tcl_DStringAppendElement(&ds
, "failed");
1767 Tcl_DStringAppendElement(&ds
, "done");
1770 Tcl_DStringAppendElement(&ds
, "connecting");
1773 Tcl_DStringAppendElement(&ds
, "aborted");
1777 Tcl_DStringAppendElement(&ds
, (const char *) xchat_list_str(ph
, list
, "nick"));
1781 Tcl_DStringAppendElement(&ds
, (const char *) xchat_list_str(ph
, list
, "file"));
1784 Tcl_DStringAppendElement(&ds
, (const char *) xchat_list_str(ph
, list
, "destfile"));
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
);
1807 static int tcl_strip(ClientData cd
, Tcl_Interp
* irp
, int argc
, const char *argv
[])
1812 BADARGS(2, 3, " text ?flags?");
1815 if (Tcl_GetInt(irp
, argv
[2], &flags
) != TCL_OK
)
1819 new_text
= xchat_strip(ph
, argv
[1], -1, flags
);
1822 Tcl_AppendResult(irp
, new_text
, NULL
);
1823 xchat_free(ph
, new_text
);
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
);
1841 ctx
= xchat_smart_context(argv
[1], NULL
);
1844 ctx
= xchat_smart_context(argv
[1], argv
[2]);
1851 xchat_set_context(ph
, ctx
);
1852 Tcl_AppendResult(irp
, xchat_get_info(ph
, "topic"), NULL
);
1853 xchat_set_context(ph
, origctx
);
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
);
1865 static int tcl_word(ClientData cd
, Tcl_Interp
* irp
, int argc
, const char *argv
[])
1869 BADARGS(2, 2, " index");
1871 if (Tcl_GetInt(irp
, argv
[1], &index
) != TCL_OK
)
1874 if (!index
|| (index
> 31))
1875 Tcl_AppendResult(interp
, "", NULL
);
1877 Tcl_AppendResult(interp
, complete
[complete_level
].word
[index
], NULL
);
1882 static int tcl_word_eol(ClientData cd
, Tcl_Interp
* irp
, int argc
, const char *argv
[])
1886 BADARGS(2, 2, " index");
1888 if (Tcl_GetInt(irp
, argv
[1], &index
) != TCL_OK
)
1891 if (!index
|| (index
> 31))
1892 Tcl_AppendResult(interp
, "", NULL
);
1894 Tcl_AppendResult(interp
, complete
[complete_level
].word_eol
[index
], NULL
);
1899 static int Command_Alias(char *word
[], char *word_eol
[], void *userdata
)
1902 Tcl_HashEntry
*entry
;
1903 xchat_context
*origctx
;
1907 if (complete_level
== MAX_COMPLETES
)
1908 return XCHAT_EAT_NONE
;
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
);
1927 xchat_set_context(ph
, origctx
);
1932 return complete
[complete_level
--].result
;
1935 static int Null_Command_Alias(char *word
[], char *word_eol
[], void *userdata
)
1938 Tcl_HashEntry
*entry
;
1939 xchat_context
*origctx
;
1941 const char *channel
;
1944 static int recurse
= 0;
1947 return XCHAT_EAT_NONE
;
1949 if (complete_level
== MAX_COMPLETES
)
1950 return XCHAT_EAT_NONE
;
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
;
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
);
1976 xchat_set_context(ph
, origctx
);
1983 return complete
[complete_level
--].result
;
1986 static int Command_TCL(char *word
[], char *word_eol
[], void *userdata
)
1988 const char *errorInfo
;
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
);
1998 xchat_printf(ph
, "\0039Tcl plugin\003\tRESULT: %s ", Tcl_GetStringResult(interp
));
2002 return XCHAT_EAT_ALL
;
2005 static int Command_Source(char *word
[], char *word_eol
[], void *userdata
)
2007 const char *xchatdir
;
2011 const char *errorInfo
;
2013 if (!strlen(word_eol
[2]))
2014 return XCHAT_EAT_NONE
;
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]));
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
);
2042 xchat_printf(ph
, "\0039Tcl plugin\003\tSourced %s\n", ds
.string
);
2044 Tcl_DStringFree(&ds
);
2048 return XCHAT_EAT_XCHAT
;
2052 return XCHAT_EAT_NONE
;
2057 static int Command_Reloadall(char *word
[], char *word_eol
[], void *userdata
)
2059 Tcl_Plugin_DeInit();
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
);
2074 static void Tcl_Plugin_Init()
2077 const char *xchatdir
;
2079 interp
= Tcl_CreateInterp();
2081 Tcl_FindExecutable(NULL
);
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
));
2127 for (x
= 0; x
< XC_SIZE
; x
++)
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()
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
);
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
);
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
;
2187 for (x
= 0; x
< XC_SIZE
; x
++) {
2188 if (xc
[x
].hook
!= NULL
) {
2189 xchat_unhook(ph
, xc
[x
].hook
);
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);
2203 if (initialized
!= 0) {
2204 xchat_print(ph
, "Tcl plugin already loaded");
2210 *plugin_name
= "Tcl";
2211 *plugin_desc
= "Tcl scripting interface";
2212 *plugin_version
= VERSION
;
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()
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");
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";