/[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.3.2.5 - (hide annotations) (download) (as text)
Sat Jun 16 15:59:49 2012 UTC (7 years, 3 months ago) by thommey
Branch: gettext
Changes since 1.3.2.4: +2 -1 lines
File MIME type: text/x-chdr
Call Tcl's bgerror on Eggdrop background errors.

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

webmaster@eggheads.org
ViewVC Help
Powered by ViewVC 1.1.23