/[cvs]/eggdrop1.8/src/tclhash.c
ViewVC logotype

Annotation of /eggdrop1.8/src/tclhash.c

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.2 - (hide annotations) (download) (as text)
Tue Oct 19 12:13:33 2010 UTC (10 years ago) by pseudo
Branch: MAIN
Changes since 1.1: +35 -1 lines
File MIME type: text/x-chdr
Added full SSL support including Tcl commands.
Added support for certificate authentication.
Added support for botnet and partyline encryption using ssl.
Documented the new features and commands.
Fixed add_server() problems with IPv6 addresses in the server list.

1 simple 1.1 /*
2     * tclhash.c -- handles:
3     * bind and unbind
4     * checking and triggering the various in-bot bindings
5     * listing current bindings
6     * adding/removing new binding tables
7     * (non-Tcl) procedure lookups for msg/dcc/file commands
8     * (Tcl) binding internal procedures to msg/dcc/file commands
9     *
10 pseudo 1.2 * $Id: tclhash.c,v 1.1.1.1 2010/07/26 21:11:06 simple Exp $
11 simple 1.1 */
12     /*
13     * Copyright (C) 1997 Robey Pointer
14     * Copyright (C) 1999 - 2010 Eggheads Development Team
15     *
16     * This program is free software; you can redistribute it and/or
17     * modify it under the terms of the GNU General Public License
18     * as published by the Free Software Foundation; either version 2
19     * of the License, or (at your option) any later version.
20     *
21     * This program is distributed in the hope that it will be useful,
22     * but WITHOUT ANY WARRANTY; without even the implied warranty of
23     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24     * GNU General Public License for more details.
25     *
26     * You should have received a copy of the GNU General Public License
27     * along with this program; if not, write to the Free Software
28     * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29     */
30    
31     #include "main.h"
32     #include "chan.h"
33     #include "users.h"
34    
35     extern Tcl_Interp *interp;
36     extern struct dcc_t *dcc;
37     extern struct userrec *userlist;
38     extern int dcc_total;
39     extern time_t now;
40    
41     p_tcl_bind_list bind_table_list;
42     p_tcl_bind_list H_chat, H_act, H_bcst, H_chon, H_chof, H_load, H_unld, H_link,
43     H_disc, H_dcc, H_chjn, H_chpt, H_bot, H_time, H_nkch, H_away,
44     H_note, H_filt, H_event, H_cron, H_log = NULL;
45 pseudo 1.2 #ifdef TLS
46     p_tcl_bind_list H_tls = NULL;
47     static int builtin_idx();
48     #endif
49 simple 1.1
50     static int builtin_2char();
51     static int builtin_3char();
52     static int builtin_5int();
53     static int builtin_cron();
54     static int builtin_char();
55     static int builtin_chpt();
56     static int builtin_chjn();
57     static int builtin_idxchar();
58     static int builtin_charidx();
59     static int builtin_chat();
60     static int builtin_dcc();
61     static int builtin_log();
62    
63    
64     /* Allocate and initialise a chunk of memory.
65     */
66     static inline void *n_malloc_null(int size, const char *file, int line)
67     {
68     #ifdef DEBUG_MEM
69     # define nmalloc_null(size) n_malloc_null(size, __FILE__, __LINE__)
70     void *ptr = n_malloc(size, file, line);
71     #else
72     # define nmalloc_null(size) n_malloc_null(size, NULL, 0)
73     void *ptr = nmalloc(size);
74     #endif
75    
76     egg_memset(ptr, 0, size);
77     return ptr;
78     }
79    
80    
81     /* Delete trigger/command.
82     */
83     static inline void tcl_cmd_delete(tcl_cmd_t *tc)
84     {
85     nfree(tc->func_name);
86     nfree(tc);
87     }
88    
89     /* Delete bind and its elements.
90     */
91     static inline void tcl_bind_mask_delete(tcl_bind_mask_t *tm)
92     {
93     tcl_cmd_t *tc, *tc_next;
94    
95     for (tc = tm->first; tc; tc = tc_next) {
96     tc_next = tc->next;
97     tcl_cmd_delete(tc);
98     }
99     nfree(tm->mask);
100     nfree(tm);
101     }
102    
103     /* Delete bind list and its elements.
104     */
105     static inline void tcl_bind_list_delete(tcl_bind_list_t *tl)
106     {
107     tcl_bind_mask_t *tm, *tm_next;
108    
109     for (tm = tl->first; tm; tm = tm_next) {
110     tm_next = tm->next;
111     tcl_bind_mask_delete(tm);
112     }
113     nfree(tl);
114     }
115    
116     inline void garbage_collect_tclhash(void)
117     {
118     tcl_bind_list_t *tl, *tl_next, *tl_prev;
119     tcl_bind_mask_t *tm, *tm_next, *tm_prev;
120     tcl_cmd_t *tc, *tc_next, *tc_prev;
121    
122     for (tl = bind_table_list, tl_prev = NULL; tl; tl = tl_next) {
123     tl_next = tl->next;
124    
125     if (tl->flags & HT_DELETED) {
126     if (tl_prev)
127     tl_prev->next = tl->next;
128     else
129     bind_table_list = tl->next;
130     tcl_bind_list_delete(tl);
131     } else {
132     for (tm = tl->first, tm_prev = NULL; tm; tm = tm_next) {
133     tm_next = tm->next;
134    
135     if (!(tm->flags & TBM_DELETED)) {
136     for (tc = tm->first, tc_prev = NULL; tc; tc = tc_next) {
137     tc_next = tc->next;
138    
139     if (tc->attributes & TC_DELETED) {
140     if (tc_prev)
141     tc_prev->next = tc->next;
142     else
143     tm->first = tc->next;
144     tcl_cmd_delete(tc);
145     } else
146     tc_prev = tc;
147     }
148     }
149    
150     /* Delete the bind when it's marked as deleted or when it's empty. */
151     if ((tm->flags & TBM_DELETED) || tm->first == NULL) {
152     if (tm_prev)
153     tm_prev->next = tm->next;
154     else
155     tl->first = tm_next;
156     tcl_bind_mask_delete(tm);
157     } else
158     tm_prev = tm;
159     }
160     tl_prev = tl;
161     }
162     }
163     }
164    
165     static inline int tcl_cmd_expmem(tcl_cmd_t *tc)
166     {
167     int tot;
168    
169     tot = sizeof(*tc);
170     if (tc->func_name)
171     tot += strlen(tc->func_name) + 1;
172     return tot;
173     }
174    
175     static inline int tcl_bind_mask_expmem(tcl_bind_mask_t *tm)
176     {
177     int tot = 0;
178     tcl_cmd_t *tc;
179    
180     for (tc = tm->first; tc; tc = tc->next)
181     tot += tcl_cmd_expmem(tc);
182     if (tm->mask)
183     tot += strlen(tm->mask) + 1;
184     tot += sizeof(*tm);
185     return tot;
186     }
187    
188     static inline int tcl_bind_list_expmem(tcl_bind_list_t *tl)
189     {
190     int tot = 0;
191     tcl_bind_mask_t *tm;
192    
193     for (tm = tl->first; tm; tm = tm->next)
194     tot += tcl_bind_mask_expmem(tm);
195     tot += sizeof(*tl);
196     return tot;
197     }
198    
199     int expmem_tclhash(void)
200     {
201     int tot = 0;
202     tcl_bind_list_t *tl;
203    
204     for (tl = bind_table_list; tl; tl = tl->next)
205     tot += tcl_bind_list_expmem(tl);
206     return tot;
207     }
208    
209    
210     extern cmd_t C_dcc[];
211     static int tcl_bind();
212    
213     static cd_tcl_cmd cd_cmd_table[] = {
214     {"bind", tcl_bind, (void *) 0},
215     {"unbind", tcl_bind, (void *) 1},
216     {0}
217     };
218    
219     void init_bind(void)
220     {
221     bind_table_list = NULL;
222     Context;
223     add_cd_tcl_cmds(cd_cmd_table);
224     H_unld = add_bind_table("unld", HT_STACKABLE, builtin_char);
225     H_time = add_bind_table("time", HT_STACKABLE, builtin_5int);
226     H_cron = add_bind_table("cron", HT_STACKABLE, builtin_cron);
227     H_note = add_bind_table("note", 0, builtin_3char);
228     H_nkch = add_bind_table("nkch", HT_STACKABLE, builtin_2char);
229     H_load = add_bind_table("load", HT_STACKABLE, builtin_char);
230     H_link = add_bind_table("link", HT_STACKABLE, builtin_2char);
231     H_filt = add_bind_table("filt", HT_STACKABLE, builtin_idxchar);
232     H_disc = add_bind_table("disc", HT_STACKABLE, builtin_char);
233     H_dcc = add_bind_table("dcc", 0, builtin_dcc);
234     H_chpt = add_bind_table("chpt", HT_STACKABLE, builtin_chpt);
235     H_chon = add_bind_table("chon", HT_STACKABLE, builtin_charidx);
236     H_chof = add_bind_table("chof", HT_STACKABLE, builtin_charidx);
237     H_chjn = add_bind_table("chjn", HT_STACKABLE, builtin_chjn);
238     H_chat = add_bind_table("chat", HT_STACKABLE, builtin_chat);
239     H_bot = add_bind_table("bot", 0, builtin_3char);
240     H_bcst = add_bind_table("bcst", HT_STACKABLE, builtin_chat);
241     H_away = add_bind_table("away", HT_STACKABLE, builtin_chat);
242     H_act = add_bind_table("act", HT_STACKABLE, builtin_chat);
243     H_event = add_bind_table("evnt", HT_STACKABLE, builtin_char);
244     H_log = add_bind_table("log", HT_STACKABLE, builtin_log);
245 pseudo 1.2 #ifdef TLS
246     H_tls = add_bind_table("tls", HT_STACKABLE, builtin_idx);
247     #endif
248 simple 1.1 add_builtins(H_dcc, C_dcc);
249     Context;
250     }
251    
252     void kill_bind(void)
253     {
254     tcl_bind_list_t *tl, *tl_next;
255    
256     rem_builtins(H_dcc, C_dcc);
257     for (tl = bind_table_list; tl; tl = tl_next) {
258     tl_next = tl->next;
259    
260     if (!(tl->flags |= HT_DELETED))
261     putlog(LOG_DEBUG, "*", "De-Allocated bind table %s", tl->name);
262     tcl_bind_list_delete(tl);
263     }
264     H_log = NULL;
265     bind_table_list = NULL;
266     }
267    
268     tcl_bind_list_t *add_bind_table(const char *nme, int flg, IntFunc func)
269     {
270     tcl_bind_list_t *tl, *tl_prev;
271     int v;
272    
273     /* Do not allow coders to use bind table names longer than
274     * 4 characters. */
275     Assert(strlen(nme) <= 4);
276    
277     for (tl = bind_table_list, tl_prev = NULL; tl; tl_prev = tl, tl = tl->next) {
278     if (tl->flags & HT_DELETED)
279     continue;
280     v = egg_strcasecmp(tl->name, nme);
281     if (!v)
282     return tl; /* Duplicate, just return old value. */
283     if (v > 0)
284     break; /* New. Insert at start of list. */
285     }
286    
287     tl = nmalloc_null(sizeof *tl);
288     strcpy(tl->name, nme);
289     tl->flags = flg;
290     tl->func = func;
291    
292     if (tl_prev) {
293     tl->next = tl_prev->next;
294     tl_prev->next = tl;
295     } else {
296     tl->next = bind_table_list;
297     bind_table_list = tl;
298     }
299    
300     putlog(LOG_DEBUG, "*", "Allocated bind table %s (flags %d)", nme, flg);
301     return tl;
302     }
303    
304     void del_bind_table(tcl_bind_list_t *tl_which)
305     {
306     tcl_bind_list_t *tl;
307    
308     for (tl = bind_table_list; tl; tl = tl->next) {
309     if (tl->flags & HT_DELETED)
310     continue;
311     if (tl == tl_which) {
312     tl->flags |= HT_DELETED;
313     putlog(LOG_DEBUG, "*", "De-Allocated bind table %s", tl->name);
314     return;
315     }
316     }
317     putlog(LOG_DEBUG, "*", "??? Tried to delete not listed bind table ???");
318     }
319    
320     tcl_bind_list_t *find_bind_table(const char *nme)
321     {
322     tcl_bind_list_t *tl;
323     int v;
324    
325     for (tl = bind_table_list; tl; tl = tl->next) {
326     if (tl->flags & HT_DELETED)
327     continue;
328     v = egg_strcasecmp(tl->name, nme);
329     if (!v)
330     return tl;
331     if (v > 0)
332     return NULL;
333     }
334     return NULL;
335     }
336    
337     static void dump_bind_tables(Tcl_Interp *irp)
338     {
339     tcl_bind_list_t *tl;
340     u_8bit_t i;
341    
342     for (tl = bind_table_list, i = 0; tl; tl = tl->next) {
343     if (tl->flags & HT_DELETED)
344     continue;
345     if (i)
346     Tcl_AppendResult(irp, ", ", NULL);
347     else
348     i = 1;
349     Tcl_AppendResult(irp, tl->name, NULL);
350     }
351     }
352    
353     static int unbind_bind_entry(tcl_bind_list_t *tl, const char *flags,
354     const char *cmd, const char *proc)
355     {
356     tcl_bind_mask_t *tm;
357    
358     /* Search for matching bind in bind list. */
359     for (tm = tl->first; tm; tm = tm->next) {
360     if (tm->flags & TBM_DELETED)
361     continue;
362     if (!strcmp(cmd, tm->mask))
363     break; /* Found it! fall out! */
364     }
365    
366     if (tm) {
367     tcl_cmd_t *tc;
368    
369     /* Search for matching proc in bind. */
370     for (tc = tm->first; tc; tc = tc->next) {
371     if (tc->attributes & TC_DELETED)
372     continue;
373     if (!egg_strcasecmp(tc->func_name, proc)) {
374     /* Erase proc regardless of flags. */
375     tc->attributes |= TC_DELETED;
376     return 1; /* Match. */
377     }
378     }
379     }
380     return 0; /* No match. */
381     }
382    
383     /* Add command (remove old one if necessary)
384     */
385     static int bind_bind_entry(tcl_bind_list_t *tl, const char *flags,
386     const char *cmd, const char *proc)
387     {
388     tcl_cmd_t *tc;
389     tcl_bind_mask_t *tm;
390    
391     /* Search for matching bind in bind list. */
392     for (tm = tl->first; tm; tm = tm->next) {
393     if (tm->flags & TBM_DELETED)
394     continue;
395     if (!strcmp(cmd, tm->mask))
396     break; /* Found it! fall out! */
397     }
398    
399     /* Create bind if it doesn't exist yet. */
400     if (!tm) {
401     tm = nmalloc_null(sizeof *tm);
402     tm->mask = nmalloc(strlen(cmd) + 1);
403     strcpy(tm->mask, cmd);
404    
405     /* Link into linked list of binds. */
406     tm->next = tl->first;
407     tl->first = tm;
408     }
409    
410     /* Proc already defined? If so, replace. */
411     for (tc = tm->first; tc; tc = tc->next) {
412     if (tc->attributes & TC_DELETED)
413     continue;
414     if (!egg_strcasecmp(tc->func_name, proc)) {
415     tc->flags.match = FR_GLOBAL | FR_CHAN;
416     break_down_flags(flags, &(tc->flags), NULL);
417     return 1;
418     }
419     }
420    
421     /* If this bind list is not stackable, remove the
422     * old entry from this bind. */
423     if (!(tl->flags & HT_STACKABLE)) {
424     for (tc = tm->first; tc; tc = tc->next) {
425     if (tc->attributes & TC_DELETED)
426     continue;
427     /* NOTE: We assume there's only one not-yet-deleted entry. */
428     tc->attributes |= TC_DELETED;
429     break;
430     }
431     }
432    
433     tc = nmalloc_null(sizeof *tc);
434     tc->flags.match = FR_GLOBAL | FR_CHAN;
435     break_down_flags(flags, &(tc->flags), NULL);
436     tc->func_name = nmalloc(strlen(proc) + 1);
437     strcpy(tc->func_name, proc);
438    
439     /* Link into linked list of the bind's command list. */
440     tc->next = tm->first;
441     tm->first = tc;
442    
443     return 1;
444     }
445    
446     static int tcl_getbinds(tcl_bind_list_t *tl_kind, const char *name)
447     {
448     tcl_bind_mask_t *tm;
449    
450     for (tm = tl_kind->first; tm; tm = tm->next) {
451     if (tm->flags & TBM_DELETED)
452     continue;
453     if (!egg_strcasecmp(tm->mask, name)) {
454     tcl_cmd_t *tc;
455    
456     for (tc = tm->first; tc; tc = tc->next) {
457     if (tc->attributes & TC_DELETED)
458     continue;
459     Tcl_AppendElement(interp, tc->func_name);
460     }
461     break;
462     }
463     }
464     return TCL_OK;
465     }
466    
467     static int tcl_bind STDVAR
468     {
469     tcl_bind_list_t *tl;
470    
471     /* cd defines what tcl_bind is supposed do: 0 = bind, 1 = unbind. */
472     if ((long int) cd == 1)
473     BADARGS(5, 5, " type flags cmd/mask procname");
474    
475     else
476     BADARGS(4, 5, " type flags cmd/mask ?procname?");
477    
478     tl = find_bind_table(argv[1]);
479     if (!tl) {
480     Tcl_AppendResult(irp, "bad type, should be one of: ", NULL);
481     dump_bind_tables(irp);
482     return TCL_ERROR;
483     }
484     if ((long int) cd == 1) {
485     if (!unbind_bind_entry(tl, argv[2], argv[3], argv[4])) {
486     /* Don't error if trying to re-unbind a builtin */
487     if (argv[4][0] != '*' || argv[4][4] != ':' ||
488     strcmp(argv[3], &argv[4][5]) || strncmp(argv[1], &argv[4][1], 3)) {
489     Tcl_AppendResult(irp, "no such binding", NULL);
490     return TCL_ERROR;
491     }
492     }
493     } else {
494     if (argc == 4)
495     return tcl_getbinds(tl, argv[3]);
496     bind_bind_entry(tl, argv[2], argv[3], argv[4]);
497     }
498     Tcl_AppendResult(irp, argv[3], NULL);
499     return TCL_OK;
500     }
501    
502     int check_validity(char *nme, IntFunc func)
503     {
504     char *p;
505     tcl_bind_list_t *tl;
506    
507     if (*nme != '*')
508     return 0;
509     p = strchr(nme + 1, ':');
510     if (p == NULL)
511     return 0;
512     *p = 0;
513     tl = find_bind_table(nme + 1);
514     *p = ':';
515     if (!tl)
516     return 0;
517     if (tl->func != func)
518     return 0;
519     return 1;
520     }
521    
522     static int builtin_3char STDVAR
523     {
524     Function F = (Function) cd;
525    
526     BADARGS(4, 4, " from to args");
527    
528     CHECKVALIDITY(builtin_3char);
529     F(argv[1], argv[2], argv[3]);
530     return TCL_OK;
531     }
532    
533     static int builtin_2char STDVAR
534     {
535     Function F = (Function) cd;
536    
537     BADARGS(3, 3, " nick msg");
538    
539     CHECKVALIDITY(builtin_2char);
540     F(argv[1], argv[2]);
541     return TCL_OK;
542     }
543    
544     static int builtin_5int STDVAR
545     {
546     Function F = (Function) cd;
547    
548     BADARGS(6, 6, " min hrs dom mon year");
549    
550     CHECKVALIDITY(builtin_5int);
551     F(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]), atoi(argv[4]), atoi(argv[5]));
552     return TCL_OK;
553     }
554    
555     static int builtin_cron STDVAR
556     {
557     Function F = (Function) cd;
558    
559     BADARGS(6, 6, " min hrs dom mon weekday");
560    
561     CHECKVALIDITY(builtin_cron);
562     F(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]), atoi(argv[4]), atoi(argv[5]));
563     return TCL_OK;
564     }
565    
566     static int builtin_char STDVAR
567     {
568     Function F = (Function) cd;
569    
570     BADARGS(2, 2, " handle");
571    
572     CHECKVALIDITY(builtin_char);
573     F(argv[1]);
574     return TCL_OK;
575     }
576    
577     static int builtin_chpt STDVAR
578     {
579     Function F = (Function) cd;
580    
581     BADARGS(3, 3, " bot nick sock");
582    
583     CHECKVALIDITY(builtin_chpt);
584     F(argv[1], argv[2], atoi(argv[3]));
585     return TCL_OK;
586     }
587    
588     static int builtin_chjn STDVAR
589     {
590     Function F = (Function) cd;
591    
592     BADARGS(6, 6, " bot nick chan# flag&sock host");
593    
594     CHECKVALIDITY(builtin_chjn);
595     F(argv[1], argv[2], atoi(argv[3]), argv[4][0],
596     argv[4][0] ? atoi(argv[4] + 1) : 0, argv[5]);
597     return TCL_OK;
598     }
599    
600     static int builtin_idxchar STDVAR
601     {
602     Function F = (Function) cd;
603     int idx;
604     char *r;
605    
606     BADARGS(3, 3, " idx args");
607    
608     CHECKVALIDITY(builtin_idxchar);
609     idx = findidx(atoi(argv[1]));
610     if (idx < 0) {
611     Tcl_AppendResult(irp, "invalid idx", NULL);
612     return TCL_ERROR;
613     }
614     r = (((char *(*)()) F) (idx, argv[2]));
615    
616     Tcl_ResetResult(irp);
617     Tcl_AppendResult(irp, r, NULL);
618     return TCL_OK;
619     }
620    
621     static int builtin_charidx STDVAR
622     {
623     Function F = (Function) cd;
624     int idx;
625    
626     BADARGS(3, 3, " handle idx");
627    
628     CHECKVALIDITY(builtin_charidx);
629     idx = findanyidx(atoi(argv[2]));
630     if (idx < 0) {
631     Tcl_AppendResult(irp, "invalid idx", NULL);
632     return TCL_ERROR;
633     }
634     Tcl_AppendResult(irp, int_to_base10(F(argv[1], idx)), NULL);
635    
636     return TCL_OK;
637     }
638    
639     static int builtin_chat STDVAR
640     {
641     Function F = (Function) cd;
642     int ch;
643    
644     BADARGS(4, 4, " handle idx text");
645    
646     CHECKVALIDITY(builtin_chat);
647     ch = atoi(argv[2]);
648     F(argv[1], ch, argv[3]);
649     return TCL_OK;
650     }
651    
652     static int builtin_dcc STDVAR
653     {
654     int idx;
655     Function F = (Function) cd;
656    
657     BADARGS(4, 4, " hand idx param");
658    
659     CHECKVALIDITY(builtin_dcc);
660     idx = findidx(atoi(argv[2]));
661     if (idx < 0) {
662     Tcl_AppendResult(irp, "invalid idx", NULL);
663     return TCL_ERROR;
664     }
665    
666     /* FIXME: This is an ugly hack. It is not documented as a
667     * 'feature' because it will eventually go away.
668     */
669     if (F == CMD_LEAVE) {
670     Tcl_AppendResult(irp, "break", NULL);
671     return TCL_OK;
672     }
673    
674     /* Check if it's a password change, if so, don't show the password. We
675     * don't need pretty formats here, as it's only for debugging purposes.
676     */
677     debug4("tcl: builtin dcc call: %s %s %s %s", argv[0], argv[1], argv[2],
678     (!strcmp(argv[0] + 5, "newpass") || !strcmp(argv[0] + 5, "chpass")) ?
679     "[something]" : argv[3]);
680     F(dcc[idx].user, idx, argv[3]);
681     Tcl_ResetResult(irp);
682     Tcl_AppendResult(irp, "0", NULL);
683     return TCL_OK;
684     }
685    
686     static int builtin_log STDVAR
687     {
688     Function F = (Function) cd;
689    
690     BADARGS(3, 3, " lvl chan msg");
691    
692     CHECKVALIDITY(builtin_log);
693     F(argv[1], argv[2], argv[3]);
694     return TCL_OK;
695     }
696    
697 pseudo 1.2 #ifdef TLS
698     static int builtin_idx STDVAR
699     {
700     Function F = (Function) cd;
701    
702     BADARGS(2, 2, " idx");
703    
704     CHECKVALIDITY(builtin_idx);
705     F(atoi(argv[1]));
706     return TCL_OK;
707     }
708     #endif
709    
710 simple 1.1 /* Trigger (execute) a Tcl proc
711     *
712     * Note: This is INLINE code for check_tcl_bind().
713     */
714     static inline int trigger_bind(const char *proc, const char *param,
715     char *mask)
716     {
717     int x;
718     #ifdef DEBUG_CONTEXT
719     const char *msg = "Tcl proc: %s, param: %s";
720     char *buf;
721    
722     /* We now try to debug the Tcl_VarEval() call below by remembering both
723     * the called proc name and it's parameters. This should render us a bit
724     * less helpless when we see context dumps.
725     */
726     Context;
727     buf = nmalloc(strlen(msg) + (proc ? strlen(proc) : 6)
728     + (param ? strlen(param) : 6) + 1);
729     sprintf(buf, msg, proc ? proc : "<null>", param ? param : "<null>");
730     ContextNote(buf);
731     nfree(buf);
732     #endif /* DEBUG_CONTEXT */
733    
734     /* Set the lastbind variable before evaluating the proc so that the name
735     * of the command that triggered the bind will be available to the proc.
736     * This feature is used by scripts such as userinfo.tcl
737     */
738     Tcl_SetVar(interp, "lastbind", (char *) mask, TCL_GLOBAL_ONLY);
739    
740     x = Tcl_VarEval(interp, proc, param, NULL);
741     Context;
742    
743     if (x == TCL_ERROR) {
744     /* FIXME: we really should be able to log longer errors */
745     putlog(LOG_MISC, "*", "Tcl error [%s]: %.*s", proc, 400, tcl_resultstring());
746    
747     return BIND_EXECUTED;
748     }
749    
750     /* FIXME: This is an ugly hack. It is not documented as a
751     * 'feature' because it will eventually go away.
752     */
753     if (!strcmp(tcl_resultstring(), "break"))
754     return BIND_QUIT;
755    
756     return (tcl_resultint() > 0) ? BIND_EXEC_LOG : BIND_EXECUTED;
757     }
758    
759    
760     /* Find out whether this bind matches the mask or provides the
761     * requested attributes, depending on the specified requirements.
762     *
763     * Note: This is INLINE code for check_tcl_bind().
764     */
765     static inline int check_bind_match(const char *match, char *mask,
766     int match_type)
767     {
768     switch (match_type & 0x07) {
769     case MATCH_PARTIAL:
770     return (!egg_strncasecmp(match, mask, strlen(match)));
771     break;
772     case MATCH_EXACT:
773     return (!egg_strcasecmp(match, mask));
774     break;
775     case MATCH_CASE:
776     return (!strcmp(match, mask));
777     break;
778     case MATCH_MASK:
779     return (wild_match_per(mask, match));
780     break;
781     case MATCH_MODE:
782     return (wild_match_partial_case(mask, match));
783     break;
784     case MATCH_CRON:
785     return (cron_match(mask, match));
786     break;
787     default:
788     /* Do nothing */
789     break;
790     }
791     return 0;
792     }
793    
794    
795     /* Check if the provided flags suffice for this command/trigger.
796     *
797     * Note: This is INLINE code for check_tcl_bind().
798     */
799     static inline int check_bind_flags(struct flag_record *flags,
800     struct flag_record *atr, int match_type)
801     {
802     if (match_type & BIND_USE_ATTR) {
803     if (match_type & BIND_HAS_BUILTINS)
804     return (flagrec_ok(flags, atr));
805     else
806     return (flagrec_eq(flags, atr));
807     } else
808     return 1;
809     return 0;
810     }
811    
812    
813     /* Check for and process Tcl binds */
814     int check_tcl_bind(tcl_bind_list_t *tl, const char *match,
815     struct flag_record *atr, const char *param, int match_type)
816     {
817     int x, result = 0, cnt = 0, finish = 0;
818     char *proc = NULL, *mask = NULL;
819     tcl_bind_mask_t *tm, *tm_last = NULL, *tm_p = NULL;
820     tcl_cmd_t *tc, *htc = NULL;
821    
822     for (tm = tl->first; tm && !finish; tm_last = tm, tm = tm->next) {
823    
824     if (tm->flags & TBM_DELETED)
825     continue; /* This bind mask was deleted */
826    
827     if (!check_bind_match(match, tm->mask, match_type))
828     continue; /* This bind does not match. */
829    
830     for (tc = tm->first; tc; tc = tc->next) {
831    
832     /* Search for valid entry. */
833     if (!(tc->attributes & TC_DELETED)) {
834    
835     /* Check if the provided flags suffice for this command. */
836     if (check_bind_flags(&tc->flags, atr, match_type)) {
837     cnt++;
838     tm_p = tm_last;
839    
840     /* Not stackable */
841     if (!(match_type & BIND_STACKABLE)) {
842    
843     /* Remember information about this bind. */
844     proc = tc->func_name;
845     mask = tm->mask;
846     htc = tc;
847    
848     /* Either this is a non-partial match, which means we
849     * only want to execute _one_ bind ...
850     */
851     if ((match_type & 0x07) != MATCH_PARTIAL ||
852     /* ... or this happens to be an exact match. */
853     !egg_strcasecmp(match, tm->mask)) {
854     cnt = 1;
855     finish = 1;
856     }
857    
858     /* We found a match so break out of the inner loop. */
859     break;
860     }
861    
862     /*
863     * Stackable; could be multiple commands/triggers.
864     * Note: This code assumes BIND_ALTER_ARGS, BIND_WANTRET, and
865     * BIND_STACKRET will only be used for stackable binds.
866     */
867    
868     /* We will only return if BIND_ALTER_ARGS or BIND_WANTRET was
869     * specified because we want to trigger all binds in a stack.
870     */
871    
872     tc->hits++;
873     x = trigger_bind(tc->func_name, param, tm->mask);
874    
875     if (match_type & BIND_ALTER_ARGS) {
876     if (tcl_resultempty())
877     return x;
878     } else if ((match_type & BIND_STACKRET) && x == BIND_EXEC_LOG) {
879     /* If we have multiple commands/triggers, and if any of the
880     * commands return 1, we store the result so we can return it
881     * after processing all stacked binds.
882     */
883     if (!result)
884     result = x;
885     continue;
886     } else if ((match_type & BIND_WANTRET) && x == BIND_EXEC_LOG)
887     /* Return immediately if any commands return 1 */
888     return x;
889     }
890     }
891     }
892     }
893    
894     if (!cnt)
895     return BIND_NOMATCH;
896    
897     /* Do this before updating the preferred entries information,
898     * since we don't want to change the order of stacked binds
899     */
900     if (result) /* BIND_STACKRET */
901     return result;
902    
903     if ((match_type & 0x07) == MATCH_MASK || (match_type & 0x07) == MATCH_CASE)
904     return BIND_EXECUTED;
905    
906     /* Hit counter */
907     if (htc)
908     htc->hits++;
909    
910     /* Now that we have found at least one bind, we can update the
911     * preferred entries information.
912     */
913     if (tm_p && tm_p->next) {
914     tm = tm_p->next; /* Move mask to front of bind's mask list. */
915     tm_p->next = tm->next; /* Unlink mask from list. */
916     tm->next = tl->first; /* Readd mask to front of list. */
917     tl->first = tm;
918     }
919    
920     if (cnt > 1)
921     return BIND_AMBIGUOUS;
922    
923     return trigger_bind(proc, param, mask);
924     }
925    
926    
927     /* Check for tcl-bound dcc command, return 1 if found
928     * dcc: proc-name <handle> <sock> <args...>
929     */
930     int check_tcl_dcc(const char *cmd, int idx, const char *args)
931     {
932     struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 };
933     int x;
934     char s[11];
935    
936     get_user_flagrec(dcc[idx].user, &fr, dcc[idx].u.chat->con_chan);
937     egg_snprintf(s, sizeof s, "%ld", dcc[idx].sock);
938     Tcl_SetVar(interp, "_dcc1", (char *) dcc[idx].nick, 0);
939     Tcl_SetVar(interp, "_dcc2", (char *) s, 0);
940     Tcl_SetVar(interp, "_dcc3", (char *) args, 0);
941     x = check_tcl_bind(H_dcc, cmd, &fr, " $_dcc1 $_dcc2 $_dcc3",
942     MATCH_PARTIAL | BIND_USE_ATTR | BIND_HAS_BUILTINS);
943     if (x == BIND_AMBIGUOUS) {
944     dprintf(idx, MISC_AMBIGUOUS);
945     return 0;
946     }
947     if (x == BIND_NOMATCH) {
948     dprintf(idx, MISC_NOSUCHCMD);
949     return 0;
950     }
951    
952     /* We return 1 to leave the partyline */
953     if (x == BIND_QUIT) /* CMD_LEAVE, 'quit' */
954     return 1;
955    
956     if (x == BIND_EXEC_LOG)
957     putlog(LOG_CMDS, "*", "#%s# %s %s", dcc[idx].nick, cmd, args);
958     return 0;
959     }
960    
961     void check_tcl_bot(const char *nick, const char *code, const char *param)
962     {
963     Tcl_SetVar(interp, "_bot1", (char *) nick, 0);
964     Tcl_SetVar(interp, "_bot2", (char *) code, 0);
965     Tcl_SetVar(interp, "_bot3", (char *) param, 0);
966     check_tcl_bind(H_bot, code, 0, " $_bot1 $_bot2 $_bot3", MATCH_EXACT);
967     }
968    
969     void check_tcl_chonof(char *hand, int sock, tcl_bind_list_t *tl)
970     {
971     struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 };
972     char s[11];
973     struct userrec *u;
974    
975     u = get_user_by_handle(userlist, hand);
976     touch_laston(u, "partyline", now);
977     get_user_flagrec(u, &fr, NULL);
978     Tcl_SetVar(interp, "_chonof1", (char *) hand, 0);
979     egg_snprintf(s, sizeof s, "%d", sock);
980     Tcl_SetVar(interp, "_chonof2", (char *) s, 0);
981     check_tcl_bind(tl, hand, &fr, " $_chonof1 $_chonof2", MATCH_MASK |
982     BIND_USE_ATTR | BIND_STACKABLE | BIND_WANTRET);
983     }
984    
985     void check_tcl_chatactbcst(const char *from, int chan, const char *text,
986     tcl_bind_list_t *tl)
987     {
988     char s[11];
989    
990     egg_snprintf(s, sizeof s, "%d", chan);
991     Tcl_SetVar(interp, "_cab1", (char *) from, 0);
992     Tcl_SetVar(interp, "_cab2", (char *) s, 0);
993     Tcl_SetVar(interp, "_cab3", (char *) text, 0);
994     check_tcl_bind(tl, text, 0, " $_cab1 $_cab2 $_cab3",
995     MATCH_MASK | BIND_STACKABLE);
996     }
997    
998     void check_tcl_nkch(const char *ohand, const char *nhand)
999     {
1000     Tcl_SetVar(interp, "_nkch1", (char *) ohand, 0);
1001     Tcl_SetVar(interp, "_nkch2", (char *) nhand, 0);
1002     check_tcl_bind(H_nkch, ohand, 0, " $_nkch1 $_nkch2",
1003     MATCH_MASK | BIND_STACKABLE);
1004     }
1005    
1006     void check_tcl_link(const char *bot, const char *via)
1007     {
1008     Tcl_SetVar(interp, "_link1", (char *) bot, 0);
1009     Tcl_SetVar(interp, "_link2", (char *) via, 0);
1010     check_tcl_bind(H_link, bot, 0, " $_link1 $_link2",
1011     MATCH_MASK | BIND_STACKABLE);
1012     }
1013    
1014     void check_tcl_disc(const char *bot)
1015     {
1016     Tcl_SetVar(interp, "_disc1", (char *) bot, 0);
1017     check_tcl_bind(H_disc, bot, 0, " $_disc1", MATCH_MASK | BIND_STACKABLE);
1018     }
1019    
1020     void check_tcl_loadunld(const char *mod, tcl_bind_list_t *tl)
1021     {
1022     Tcl_SetVar(interp, "_lu1", (char *) mod, 0);
1023     check_tcl_bind(tl, mod, 0, " $_lu1", MATCH_MASK | BIND_STACKABLE);
1024     }
1025    
1026     const char *check_tcl_filt(int idx, const char *text)
1027     {
1028     char s[11];
1029     int x;
1030     struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 };
1031    
1032     egg_snprintf(s, sizeof s, "%ld", dcc[idx].sock);
1033     get_user_flagrec(dcc[idx].user, &fr, dcc[idx].u.chat->con_chan);
1034     Tcl_SetVar(interp, "_filt1", (char *) s, 0);
1035     Tcl_SetVar(interp, "_filt2", (char *) text, 0);
1036     x = check_tcl_bind(H_filt, text, &fr, " $_filt1 $_filt2",
1037     MATCH_MASK | BIND_USE_ATTR | BIND_STACKABLE |
1038     BIND_WANTRET | BIND_ALTER_ARGS);
1039     if (x == BIND_EXECUTED || x == BIND_EXEC_LOG) {
1040     if (tcl_resultempty())
1041     return "";
1042     else
1043     return tcl_resultstring();
1044     } else
1045     return text;
1046     }
1047    
1048     int check_tcl_note(const char *from, const char *to, const char *text)
1049     {
1050     int x;
1051    
1052     Tcl_SetVar(interp, "_note1", (char *) from, 0);
1053     Tcl_SetVar(interp, "_note2", (char *) to, 0);
1054     Tcl_SetVar(interp, "_note3", (char *) text, 0);
1055    
1056     x = check_tcl_bind(H_note, to, 0, " $_note1 $_note2 $_note3",
1057     MATCH_MASK | BIND_STACKABLE | BIND_WANTRET);
1058    
1059     return (x == BIND_EXEC_LOG);
1060     }
1061    
1062     void check_tcl_listen(const char *cmd, int idx)
1063     {
1064     char s[11];
1065     int x;
1066    
1067     egg_snprintf(s, sizeof s, "%d", idx);
1068     Tcl_SetVar(interp, "_n", (char *) s, 0);
1069     x = Tcl_VarEval(interp, cmd, " $_n", NULL);
1070     if (x == TCL_ERROR)
1071     putlog(LOG_MISC, "*", "error on listen: %s", tcl_resultstring());
1072     }
1073    
1074     void check_tcl_chjn(const char *bot, const char *nick, int chan,
1075     const char type, int sock, const char *host)
1076     {
1077     struct flag_record fr = { FR_GLOBAL, 0, 0, 0, 0, 0 };
1078     char s[11], t[2], u[11];
1079    
1080     t[0] = type;
1081     t[1] = 0;
1082     switch (type) {
1083     case '*':
1084     fr.global = USER_OWNER;
1085    
1086     break;
1087     case '+':
1088     fr.global = USER_MASTER;
1089    
1090     break;
1091     case '@':
1092     fr.global = USER_OP;
1093    
1094     break;
1095     case '^':
1096     fr.global = USER_HALFOP;
1097    
1098     break;
1099     case '%':
1100     fr.global = USER_BOTMAST;
1101     }
1102     egg_snprintf(s, sizeof s, "%d", chan);
1103     egg_snprintf(u, sizeof u, "%d", sock);
1104     Tcl_SetVar(interp, "_chjn1", (char *) bot, 0);
1105     Tcl_SetVar(interp, "_chjn2", (char *) nick, 0);
1106     Tcl_SetVar(interp, "_chjn3", (char *) s, 0);
1107     Tcl_SetVar(interp, "_chjn4", (char *) t, 0);
1108     Tcl_SetVar(interp, "_chjn5", (char *) u, 0);
1109     Tcl_SetVar(interp, "_chjn6", (char *) host, 0);
1110     check_tcl_bind(H_chjn, s, &fr,
1111     " $_chjn1 $_chjn2 $_chjn3 $_chjn4 $_chjn5 $_chjn6",
1112     MATCH_MASK | BIND_STACKABLE);
1113     }
1114    
1115     void check_tcl_chpt(const char *bot, const char *hand, int sock, int chan)
1116     {
1117     char u[11], v[11];
1118    
1119     egg_snprintf(u, sizeof u, "%d", sock);
1120     egg_snprintf(v, sizeof v, "%d", chan);
1121     Tcl_SetVar(interp, "_chpt1", (char *) bot, 0);
1122     Tcl_SetVar(interp, "_chpt2", (char *) hand, 0);
1123     Tcl_SetVar(interp, "_chpt3", (char *) u, 0);
1124     Tcl_SetVar(interp, "_chpt4", (char *) v, 0);
1125     check_tcl_bind(H_chpt, v, 0, " $_chpt1 $_chpt2 $_chpt3 $_chpt4",
1126     MATCH_MASK | BIND_STACKABLE);
1127     }
1128    
1129     void check_tcl_away(const char *bot, int idx, const char *msg)
1130     {
1131     char u[11];
1132    
1133     egg_snprintf(u, sizeof u, "%d", idx);
1134     Tcl_SetVar(interp, "_away1", (char *) bot, 0);
1135     Tcl_SetVar(interp, "_away2", (char *) u, 0);
1136     Tcl_SetVar(interp, "_away3", msg ? (char *) msg : "", 0);
1137     check_tcl_bind(H_away, bot, 0, " $_away1 $_away2 $_away3",
1138     MATCH_MASK | BIND_STACKABLE);
1139     }
1140    
1141     void check_tcl_time(struct tm *tm)
1142     {
1143     char y[18];
1144    
1145     egg_snprintf(y, sizeof y, "%02d", tm->tm_min);
1146     Tcl_SetVar(interp, "_time1", (char *) y, 0);
1147     egg_snprintf(y, sizeof y, "%02d", tm->tm_hour);
1148     Tcl_SetVar(interp, "_time2", (char *) y, 0);
1149     egg_snprintf(y, sizeof y, "%02d", tm->tm_mday);
1150     Tcl_SetVar(interp, "_time3", (char *) y, 0);
1151     egg_snprintf(y, sizeof y, "%02d", tm->tm_mon);
1152     Tcl_SetVar(interp, "_time4", (char *) y, 0);
1153     egg_snprintf(y, sizeof y, "%04d", tm->tm_year + 1900);
1154     Tcl_SetVar(interp, "_time5", (char *) y, 0);
1155     egg_snprintf(y, sizeof y, "%02d %02d %02d %02d %04d", tm->tm_min, tm->tm_hour,
1156     tm->tm_mday, tm->tm_mon, tm->tm_year + 1900);
1157     check_tcl_bind(H_time, y, 0,
1158     " $_time1 $_time2 $_time3 $_time4 $_time5",
1159     MATCH_MASK | BIND_STACKABLE);
1160     }
1161    
1162     void check_tcl_cron(struct tm *tm)
1163     {
1164     char y[15];
1165    
1166     egg_snprintf(y, sizeof y, "%02d", tm->tm_min);
1167     Tcl_SetVar(interp, "_cron1", (char *) y, 0);
1168     egg_snprintf(y, sizeof y, "%02d", tm->tm_hour);
1169     Tcl_SetVar(interp, "_cron2", (char *) y, 0);
1170     egg_snprintf(y, sizeof y, "%02d", tm->tm_mday);
1171     Tcl_SetVar(interp, "_cron3", (char *) y, 0);
1172     egg_snprintf(y, sizeof y, "%02d", tm->tm_mon + 1);
1173     Tcl_SetVar(interp, "_cron4", (char *) y, 0);
1174     egg_snprintf(y, sizeof y, "%02d", tm->tm_wday);
1175     Tcl_SetVar(interp, "_cron5", (char *) y, 0);
1176     egg_snprintf(y, sizeof y, "%02d %02d %02d %02d %02d", tm->tm_min, tm->tm_hour,
1177     tm->tm_mday, tm->tm_mon + 1, tm->tm_wday);
1178     check_tcl_bind(H_cron, y, 0,
1179     " $_cron1 $_cron2 $_cron3 $_cron4 $_cron5",
1180     MATCH_CRON | BIND_STACKABLE);
1181     }
1182    
1183     void check_tcl_event(const char *event)
1184     {
1185     Tcl_SetVar(interp, "_event1", (char *) event, 0);
1186     check_tcl_bind(H_event, event, 0, " $_event1", MATCH_EXACT | BIND_STACKABLE);
1187     }
1188    
1189     void check_tcl_log(int lv, char *chan, char *msg)
1190     {
1191     char mask[512];
1192    
1193     snprintf(mask, sizeof mask, "%s %s", chan, msg);
1194     Tcl_SetVar(interp, "_log1", masktype(lv), 0);
1195     Tcl_SetVar(interp, "_log2", chan, 0);
1196     Tcl_SetVar(interp, "_log3", msg, 0);
1197     check_tcl_bind(H_log, mask, 0, " $_log1 $_log2 $_log3",
1198     MATCH_MASK | BIND_STACKABLE);
1199     }
1200    
1201 pseudo 1.2 #ifdef TLS
1202     int check_tcl_tls(int sock)
1203     {
1204     int x;
1205     char s[11];
1206    
1207     egg_snprintf(s, sizeof s, "%d", sock);
1208     Tcl_SetVar(interp, "_tls", s, 0);
1209     x = check_tcl_bind(H_tls, s, 0, " $_tls", MATCH_MASK | BIND_STACKABLE |
1210     BIND_WANTRET);
1211     return (x == BIND_EXEC_LOG);
1212     }
1213     #endif
1214    
1215 simple 1.1 void tell_binds(int idx, char *par)
1216     {
1217     tcl_bind_list_t *tl, *tl_kind;
1218     tcl_bind_mask_t *tm;
1219     int fnd = 0, showall = 0, patmatc = 0;
1220     tcl_cmd_t *tc;
1221     char *name, *proc, *s, flg[100];
1222    
1223     if (par[0])
1224     name = newsplit(&par);
1225     else
1226     name = NULL;
1227     if (par[0])
1228     s = newsplit(&par);
1229     else
1230     s = NULL;
1231    
1232     if (name)
1233     tl_kind = find_bind_table(name);
1234     else
1235     tl_kind = NULL;
1236    
1237     if ((name && name[0] && !egg_strcasecmp(name, "all")) ||
1238     (s && s[0] && !egg_strcasecmp(s, "all")))
1239     showall = 1;
1240     if (tl_kind == NULL && name && name[0] && egg_strcasecmp(name, "all"))
1241     patmatc = 1;
1242    
1243     dprintf(idx, MISC_CMDBINDS);
1244     dprintf(idx, " TYPE FLAGS COMMAND HITS BINDING (TCL)\n");
1245    
1246     for (tl = tl_kind ? tl_kind : bind_table_list; tl;
1247     tl = tl_kind ? 0 : tl->next) {
1248     if (tl->flags & HT_DELETED)
1249     continue;
1250     for (tm = tl->first; tm; tm = tm->next) {
1251     if (tm->flags & TBM_DELETED)
1252     continue;
1253     for (tc = tm->first; tc; tc = tc->next) {
1254     if (tc->attributes & TC_DELETED)
1255     continue;
1256     proc = tc->func_name;
1257     build_flags(flg, &(tc->flags), NULL);
1258     if (showall || proc[0] != '*') {
1259     int ok = 0;
1260    
1261     if (patmatc == 1) {
1262     if (wild_match_per(name, tl->name) ||
1263     wild_match_per(name, tm->mask) ||
1264     wild_match_per(name, tc->func_name))
1265     ok = 1;
1266     } else
1267     ok = 1;
1268    
1269     if (ok) {
1270     dprintf(idx, " %-4s %-8s %-20s %4d %s\n", tl->name, flg, tm->mask,
1271     tc->hits, tc->func_name);
1272     fnd = 1;
1273     }
1274     }
1275     }
1276     }
1277     }
1278     if (!fnd) {
1279     if (patmatc)
1280     dprintf(idx, "No command bindings found that match %s\n", name);
1281     else if (tl_kind)
1282     dprintf(idx, "No command bindings for type: %s.\n", name);
1283     else
1284     dprintf(idx, "No command bindings exist.\n");
1285     }
1286     }
1287    
1288     /* Bring the default msg/dcc/fil commands into the Tcl interpreter */
1289     void add_builtins(tcl_bind_list_t *tl, cmd_t *cc)
1290     {
1291     int k, i;
1292     char p[1024], *l;
1293     cd_tcl_cmd table[2];
1294    
1295     table[0].name = p;
1296     table[0].callback = tl->func;
1297     table[1].name = NULL;
1298     for (i = 0; cc[i].name; i++) {
1299     egg_snprintf(p, sizeof p, "*%s:%s", tl->name,
1300     cc[i].funcname ? cc[i].funcname : cc[i].name);
1301     l = nmalloc(Tcl_ScanElement(p, &k));
1302     Tcl_ConvertElement(p, l, k | TCL_DONT_USE_BRACES);
1303     table[0].cdata = (void *) cc[i].func;
1304     add_cd_tcl_cmds(table);
1305     bind_bind_entry(tl, cc[i].flags, cc[i].name, l);
1306     nfree(l);
1307     }
1308     }
1309    
1310     /* Remove the default msg/dcc/fil commands from the Tcl interpreter */
1311     void rem_builtins(tcl_bind_list_t *table, cmd_t *cc)
1312     {
1313     int k, i;
1314     char p[1024], *l;
1315    
1316     for (i = 0; cc[i].name; i++) {
1317     egg_snprintf(p, sizeof p, "*%s:%s", table->name,
1318     cc[i].funcname ? cc[i].funcname : cc[i].name);
1319     l = nmalloc(Tcl_ScanElement(p, &k));
1320     Tcl_ConvertElement(p, l, k | TCL_DONT_USE_BRACES);
1321     Tcl_DeleteCommand(interp, p);
1322     unbind_bind_entry(table, cc[i].flags, cc[i].name, l);
1323     nfree(l);
1324     }
1325     }

webmaster@eggheads.org
ViewVC Help
Powered by ViewVC 1.1.23