/[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.1 - (hide annotations) (download) (as text)
Mon Jul 26 21:11:06 2010 UTC (10 years, 2 months ago) by simple
Branch: MAIN
Branch point for: eggheads
File MIME type: text/x-chdr
Initial revision

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

webmaster@eggheads.org
ViewVC Help
Powered by ViewVC 1.1.23