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

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

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


Revision 1.1.1.1 - (show annotations) (download) (as text) (vendor branch)
Mon Jul 26 21:11:06 2010 UTC (8 years, 9 months ago) by simple
Branch: eggheads
CVS Tags: v1
Changes since 1.1: +0 -0 lines
File MIME type: text/x-chdr
Imported Eggdrop 1.6.20

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