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

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

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


Revision 1.3 - (show annotations) (download) (as text)
Mon Aug 23 21:27:40 2010 UTC (8 years, 10 months ago) by pseudo
Branch: MAIN
Changes since 1.2: +2 -2 lines
File MIME type: text/x-chdr
Modified src/compat/ replacements of gethostbyname2() and inet_ntop() to not compile when IPv6 is disabled.
Added a missing header preventing gethostbyname2() from compiling on FreeBSD.
Fixed few lines with wrong indentation.

1 /*
2 * tcl.c -- handles:
3 * the code for every command eggdrop adds to Tcl
4 * Tcl initialization
5 * getting and setting Tcl/eggdrop variables
6 *
7 * $Id: tcl.c,v 1.2 2010/08/05 18:12:05 pseudo Exp $
8 */
9 /*
10 * Copyright (C) 1997 Robey Pointer
11 * Copyright (C) 1999 - 2010 Eggheads Development Team
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 */
27
28 #include <stdlib.h> /* getenv() */
29 #include <locale.h> /* setlocale() */
30 #include "main.h"
31
32 /* Used for read/write to internal strings */
33 typedef struct {
34 char *str; /* Pointer to actual string in eggdrop */
35 int max; /* max length (negative: read-only var
36 * when protect is on) (0: read-only ALWAYS) */
37 int flags; /* 1 = directory */
38 } strinfo;
39
40 typedef struct {
41 int *var;
42 int ro;
43 } intinfo;
44
45
46 extern time_t online_since;
47
48 extern char origbotname[], botuser[], motdfile[], admin[], userfile[],
49 firewall[], helpdir[], notify_new[], vhost[], moddir[],
50 tempdir[], owner[], network[], botnetnick[], bannerfile[],
51 egg_version[], natip[], configfile[], logfile_suffix[], log_ts[],
52 textdir[], pid_file[], listen_ip[];
53
54
55 extern int flood_telnet_thr, flood_telnet_time, shtime, share_greet,
56 require_p, keep_all_logs, allow_new_telnets, stealth_telnets,
57 use_telnet_banner, default_flags, conmask, switch_logfiles_at,
58 connect_timeout, firewallport, notify_users_at, flood_thr, tands,
59 ignore_time, reserved_port_min, reserved_port_max, die_on_sighup,
60 die_on_sigterm, max_logs, max_logsize, dcc_total, raw_log,
61 identtimeout, dcc_sanitycheck, dupwait_timeout, egg_numver,
62 share_unlinks, protect_telnet, sort_users, strict_host,
63 resolve_timeout, default_uflags, userfile_perm, cidr_support;
64
65 #ifdef IPV6
66 extern char vhost6[];
67 extern int pref_af;
68 #endif
69
70 extern struct dcc_t *dcc;
71 extern tcl_timer_t *timer, *utimer;
72
73 Tcl_Interp *interp;
74
75 int protect_readonly = 0; /* Enable read-only protection? */
76 char whois_fields[1025] = "";
77
78 int dcc_flood_thr = 3;
79 int use_invites = 0;
80 int use_exempts = 0;
81 int force_expire = 0;
82 int remote_boots = 2;
83 int allow_dk_cmds = 1;
84 int must_be_owner = 1;
85 int quiet_reject = 1;
86 int copy_to_tmp = 1;
87 int max_socks = 100;
88 int quick_logs = 0;
89 int par_telnet_flood = 1;
90 int quiet_save = 0;
91 int strtot = 0;
92 int handlen = HANDLEN;
93 int utftot = 0;
94 int clientdata_stuff = 0;
95
96 /* Compatability for removed settings.*/
97 int strict_servernames = 0, enable_simul = 1, use_console_r = 0,
98 debug_output = 0;
99
100 /* Prototypes for Tcl */
101 Tcl_Interp *Tcl_CreateInterp();
102
103 int expmem_tcl()
104 {
105 return strtot + utftot + clientdata_stuff;
106 }
107
108 static void botnet_change(char *new)
109 {
110 if (egg_strcasecmp(botnetnick, new)) {
111 /* Trying to change bot's nickname */
112 if (tands > 0) {
113 putlog(LOG_MISC, "*", "* Tried to change my botnet nick, but I'm still "
114 "linked to a botnet.");
115 putlog(LOG_MISC, "*", "* (Unlink and try again.)");
116 return;
117 } else {
118 if (botnetnick[0])
119 putlog(LOG_MISC, "*", "* IDENTITY CHANGE: %s -> %s", botnetnick, new);
120 strcpy(botnetnick, new);
121 }
122 }
123 }
124
125
126 /*
127 * Vars, traces, misc
128 */
129
130 int init_misc();
131
132 /* Used for read/write to integer couplets */
133 typedef struct {
134 int *left; /* left side of couplet */
135 int *right; /* right side */
136 } coupletinfo;
137
138 /* FIXME: tcl_eggcouplet() should be redesigned so we can use
139 * TCL_TRACE_WRITES | TCL_TRACE_READS as the bit mask instead
140 * of 2 calls as is done in add_tcl_coups().
141 */
142 /* Read/write integer couplets (int1:int2) */
143 static char *tcl_eggcouplet(ClientData cdata, Tcl_Interp *irp,
144 EGG_CONST char *name1,
145 EGG_CONST char *name2, int flags)
146 {
147 char *s, s1[41];
148 coupletinfo *cp = (coupletinfo *) cdata;
149
150 if (flags & (TCL_TRACE_READS | TCL_TRACE_UNSETS)) {
151 egg_snprintf(s1, sizeof s1, "%d:%d", *(cp->left), *(cp->right));
152 Tcl_SetVar2(interp, name1, name2, s1, TCL_GLOBAL_ONLY);
153 if (flags & TCL_TRACE_UNSETS)
154 Tcl_TraceVar(interp, name1,
155 TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
156 tcl_eggcouplet, cdata);
157 } else { /* writes */
158 s = (char *) Tcl_GetVar2(interp, name1, name2, 0);
159 if (s != NULL) {
160 int nr1, nr2;
161
162 nr1 = nr2 = 0;
163
164 if (strlen(s) > 40)
165 s[40] = 0;
166
167 sscanf(s, "%d%*c%d", &nr1, &nr2);
168 *(cp->left) = nr1;
169 *(cp->right) = nr2;
170 }
171 }
172 return NULL;
173 }
174
175 /* Read or write normal integer.
176 */
177 static char *tcl_eggint(ClientData cdata, Tcl_Interp *irp,
178 EGG_CONST char *name1,
179 EGG_CONST char *name2, int flags)
180 {
181 char *s, s1[40];
182 long l;
183 intinfo *ii = (intinfo *) cdata;
184
185 if (flags & (TCL_TRACE_READS | TCL_TRACE_UNSETS)) {
186 /* Special cases */
187 if ((int *) ii->var == &conmask)
188 strcpy(s1, masktype(conmask));
189 else if ((int *) ii->var == &default_flags) {
190 struct flag_record fr = { FR_GLOBAL, 0, 0, 0, 0, 0 };
191 fr.global = default_flags;
192
193 fr.udef_global = default_uflags;
194 build_flags(s1, &fr, 0);
195 } else if ((int *) ii->var == &userfile_perm) {
196 egg_snprintf(s1, sizeof s1, "0%o", userfile_perm);
197 } else
198 egg_snprintf(s1, sizeof s1, "%d", *(int *) ii->var);
199 Tcl_SetVar2(interp, name1, name2, s1, TCL_GLOBAL_ONLY);
200 if (flags & TCL_TRACE_UNSETS)
201 Tcl_TraceVar(interp, name1,
202 TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
203 tcl_eggint, cdata);
204 return NULL;
205 } else { /* Writes */
206 s = (char *) Tcl_GetVar2(interp, name1, name2, TCL_GLOBAL_ONLY);
207 if (s != NULL) {
208 if ((int *) ii->var == &conmask) {
209 if (s[0])
210 conmask = logmodes(s);
211 else
212 conmask = LOG_MODES | LOG_MISC | LOG_CMDS;
213 } else if ((int *) ii->var == &default_flags) {
214 struct flag_record fr = { FR_GLOBAL, 0, 0, 0, 0, 0 };
215
216 break_down_flags(s, &fr, 0);
217 default_flags = sanity_check(fr.global); /* drummer */
218
219 default_uflags = fr.udef_global;
220 } else if ((int *) ii->var == &userfile_perm) {
221 int p = oatoi(s);
222
223 if (p <= 0)
224 return "invalid userfile permissions";
225 userfile_perm = p;
226 } else if ((ii->ro == 2) || ((ii->ro == 1) && protect_readonly))
227 return "read-only variable";
228 else {
229 if (Tcl_ExprLong(interp, s, &l) == TCL_ERROR)
230 return "variable must have integer value";
231 if ((int *) ii->var == &max_socks) {
232 if (l < threaddata()->MAXSOCKS)
233 return "you can't DECREASE max-socks below current usage";
234 max_socks = l;
235 } else if ((int *) ii->var == &max_logs) {
236 if (l < max_logs)
237 return "you can't DECREASE max-logs";
238 max_logs = l;
239 init_misc();
240 } else
241 *(ii->var) = (int) l;
242 }
243 }
244 return NULL;
245 }
246 }
247
248 /* Read/write normal string variable
249 */
250 static char *tcl_eggstr(ClientData cdata, Tcl_Interp *irp,
251 EGG_CONST char *name1,
252 EGG_CONST char *name2, int flags)
253 {
254 char *s;
255 strinfo *st = (strinfo *) cdata;
256
257 if (flags & (TCL_TRACE_READS | TCL_TRACE_UNSETS)) {
258 if ((st->str == firewall) && (firewall[0])) {
259 char s1[127];
260
261 egg_snprintf(s1, sizeof s1, "%s:%d", firewall, firewallport);
262 Tcl_SetVar2(interp, name1, name2, s1, TCL_GLOBAL_ONLY);
263 } else
264 Tcl_SetVar2(interp, name1, name2, st->str, TCL_GLOBAL_ONLY);
265 if (flags & TCL_TRACE_UNSETS) {
266 Tcl_TraceVar(interp, name1, TCL_TRACE_READS | TCL_TRACE_WRITES |
267 TCL_TRACE_UNSETS, tcl_eggstr, cdata);
268 if ((st->max <= 0) && (protect_readonly || (st->max == 0)))
269 return "read-only variable"; /* it won't return the error... */
270 }
271 return NULL;
272 } else { /* writes */
273 if ((st->max <= 0) && (protect_readonly || (st->max == 0))) {
274 Tcl_SetVar2(interp, name1, name2, st->str, TCL_GLOBAL_ONLY);
275 return "read-only variable";
276 }
277 #ifdef USE_TCL_BYTE_ARRAYS
278 # undef malloc
279 # undef free
280 {
281 Tcl_Obj *obj;
282 unsigned char *bytes;
283 int len;
284
285 obj = Tcl_GetVar2Ex(interp, name1, name2, 0);
286 if (!obj)
287 return NULL;
288 len = 0;
289 bytes = Tcl_GetByteArrayFromObj(obj, &len);
290 if (!bytes)
291 return NULL;
292 s = malloc(len + 1);
293 egg_memcpy(s, bytes, len);
294 s[len] = 0;
295 }
296 #else
297 s = (char *) Tcl_GetVar2(interp, name1, name2, 0);
298 #endif /* USE_TCL_BYTE_ARRAYS */
299 if (s != NULL) {
300 if (strlen(s) > abs(st->max))
301 s[abs(st->max)] = 0;
302 if (st->str == botnetnick)
303 botnet_change(s);
304 else if (st->str == logfile_suffix)
305 logsuffix_change(s);
306 else if (st->str == firewall) {
307 splitc(firewall, s, ':');
308 if (!firewall[0])
309 strcpy(firewall, s);
310 else
311 firewallport = atoi(s);
312 } else
313 strcpy(st->str, s);
314 if ((st->flags) && (s[0])) {
315 if (st->str[strlen(st->str) - 1] != '/')
316 strcat(st->str, "/");
317 }
318 #ifdef USE_TCL_BYTE_ARRAYS
319 free(s);
320 #endif /* USE_TCL_BYTE_ARRAYS */
321 }
322 return NULL;
323 }
324 }
325
326 /* Add/remove tcl commands
327 */
328
329 #ifdef USE_TCL_BYTE_ARRAYS
330 static int utf_converter(ClientData cdata, Tcl_Interp *myinterp, int objc,
331 Tcl_Obj *CONST objv[])
332 {
333 char **strings, *byteptr;
334 int i, len, retval, diff;
335 void **callback_data;
336 Function func;
337 ClientData cd;
338
339 objc += 5;
340 strings = (char **) nmalloc(sizeof(char *) * objc);
341 egg_memset(strings, 0, sizeof(char *) * objc);
342 diff = utftot;
343 utftot += sizeof(char *) * objc;
344 objc -= 5;
345 for (i = 0; i < objc; i++) {
346 byteptr = (char *) Tcl_GetByteArrayFromObj(objv[i], &len);
347 strings[i] = (char *) nmalloc(len + 1);
348 utftot += len + 1;
349 strncpy(strings[i], byteptr, len);
350 strings[i][len] = 0;
351 }
352 callback_data = (void **) cdata;
353 func = (Function) callback_data[0];
354 cd = (ClientData) callback_data[1];
355 diff -= utftot;
356 retval = func(cd, myinterp, objc, strings);
357 for (i = 0; i < objc; i++)
358 nfree(strings[i]);
359 nfree(strings);
360 utftot += diff;
361 return retval;
362 }
363
364 void cmd_delete_callback(ClientData cdata)
365 {
366 nfree(cdata);
367 clientdata_stuff -= sizeof(void *) * 2;
368 }
369 #endif /* USE_TCL_BYTE_ARRAYS */
370
371 #ifdef USE_TCL_BYTE_ARRAYS
372 void add_tcl_commands(tcl_cmds *table)
373 {
374 void **cdata;
375
376 while (table->name) {
377 cdata = (void **) nmalloc(sizeof(void *) * 2);
378 clientdata_stuff += sizeof(void *) * 2;
379 cdata[0] = (void *)table->func;
380 cdata[1] = NULL;
381 Tcl_CreateObjCommand(interp, table->name, utf_converter, (ClientData) cdata,
382 cmd_delete_callback);
383 table++;
384 }
385 }
386
387 #else /* USE_TCL_BYTE_ARRAYS */
388
389 void add_tcl_commands(tcl_cmds *table)
390 {
391 int i;
392
393 for (i = 0; table[i].name; i++)
394 Tcl_CreateCommand(interp, table[i].name, table[i].func, NULL, NULL);
395 }
396 #endif /* USE_TCL_BYTE_ARRAYS */
397
398 #ifdef USE_TCL_BYTE_ARRAYS
399 void add_cd_tcl_cmds(cd_tcl_cmd *table)
400 {
401 void **cdata;
402
403 while (table->name) {
404 cdata = nmalloc(sizeof(void *) * 2);
405 clientdata_stuff += sizeof(void *) * 2;
406 cdata[0] = (void *)table->callback;
407 cdata[1] = table->cdata;
408 Tcl_CreateObjCommand(interp, table->name, utf_converter, (ClientData) cdata,
409 cmd_delete_callback);
410 table++;
411 }
412 }
413
414 #else /* USE_TCL_BYTE_ARRAYS */
415
416 void add_cd_tcl_cmds(cd_tcl_cmd *table)
417 {
418 while (table->name) {
419 Tcl_CreateCommand(interp, table->name, table->callback,
420 (ClientData) table->cdata, NULL);
421 table++;
422 }
423 }
424 #endif /* USE_TCL_BYTE_ARRAYS */
425
426 void rem_tcl_commands(tcl_cmds *table)
427 {
428 int i;
429
430 for (i = 0; table[i].name; i++)
431 Tcl_DeleteCommand(interp, table[i].name);
432 }
433
434 void rem_cd_tcl_cmds(cd_tcl_cmd *table)
435 {
436 while (table->name) {
437 Tcl_DeleteCommand(interp, table->name);
438 table++;
439 }
440 }
441
442 #ifdef USE_TCL_OBJ
443 void add_tcl_objcommands(tcl_cmds *table)
444 {
445 int i;
446
447 for (i = 0; table[i].name; i++)
448 Tcl_CreateObjCommand(interp, table[i].name, table[i].func, (ClientData) 0,
449 NULL);
450 }
451 #endif
452
453 /* Get the current tcl result string. */
454 const char *tcl_resultstring()
455 {
456 const char *result;
457 #ifdef USE_TCL_OBJ
458 result = Tcl_GetStringResult(interp);
459 #else
460 result = interp->result;
461 #endif
462 return result;
463 }
464
465 int tcl_resultempty() {
466 const char *result;
467 result = tcl_resultstring();
468 return (result && result[0]) ? 0 : 1;
469 }
470
471 /* Get the current tcl result as int. replaces atoi(interp->result) */
472 int tcl_resultint()
473 {
474 int result;
475 #ifdef USE_TCL_OBJ
476 if (Tcl_GetIntFromObj(NULL, Tcl_GetObjResult(interp), &result) != TCL_OK)
477 #else
478 if (Tcl_GetInt(NULL, interp->result, &result) != TCL_OK)
479 #endif
480 result = 0;
481 return result;
482 }
483
484 static tcl_strings def_tcl_strings[] = {
485 {"botnet-nick", botnetnick, HANDLEN, 0},
486 {"userfile", userfile, 120, STR_PROTECT},
487 {"motd", motdfile, 120, STR_PROTECT},
488 {"admin", admin, 120, 0},
489 {"help-path", helpdir, 120, STR_DIR | STR_PROTECT},
490 {"temp-path", tempdir, 120, STR_DIR | STR_PROTECT},
491 {"text-path", textdir, 120, STR_DIR | STR_PROTECT},
492 #ifndef STATIC
493 {"mod-path", moddir, 120, STR_DIR | STR_PROTECT},
494 #endif
495 {"notify-newusers", notify_new, 120, 0},
496 {"owner", owner, 120, STR_PROTECT},
497 {"vhost4", vhost, 120, 0},
498 #ifdef IPV6
499 {"vhost6", vhost6, 120, 0},
500 #endif
501 {"listen-addr", listen_ip, 120, 0},
502 {"network", network, 40, 0},
503 {"whois-fields", whois_fields, 1024, 0},
504 {"nat-ip", natip, 120, 0},
505 {"username", botuser, 10, 0},
506 {"version", egg_version, 0, 0},
507 {"firewall", firewall, 120, 0},
508 {"config", configfile, 0, 0},
509 {"telnet-banner", bannerfile, 120, STR_PROTECT},
510 {"logfile-suffix", logfile_suffix, 20, 0},
511 {"timestamp-format",log_ts, 32, 0},
512 {"pidfile", pid_file, 120, STR_PROTECT},
513 {NULL, NULL, 0, 0}
514 };
515
516 static tcl_ints def_tcl_ints[] = {
517 {"ignore-time", &ignore_time, 0},
518 {"handlen", &handlen, 2},
519 {"dcc-flood-thr", &dcc_flood_thr, 0},
520 {"hourly-updates", &notify_users_at, 0},
521 {"switch-logfiles-at", &switch_logfiles_at, 0},
522 {"connect-timeout", &connect_timeout, 0},
523 {"reserved-port", &reserved_port_min, 0},
524 {"require-p", &require_p, 0},
525 {"keep-all-logs", &keep_all_logs, 0},
526 {"open-telnets", &allow_new_telnets, 0},
527 {"stealth-telnets", &stealth_telnets, 0},
528 {"use-telnet-banner", &use_telnet_banner, 0},
529 {"uptime", (int *) &online_since, 2},
530 {"console", &conmask, 0},
531 {"default-flags", &default_flags, 0},
532 {"numversion", &egg_numver, 2},
533 {"die-on-sighup", &die_on_sighup, 1},
534 {"die-on-sigterm", &die_on_sigterm, 1},
535 {"remote-boots", &remote_boots, 1},
536 {"max-socks", &max_socks, 0},
537 {"max-logs", &max_logs, 0},
538 {"max-logsize", &max_logsize, 0},
539 {"quick-logs", &quick_logs, 0},
540 {"raw-log", &raw_log, 1},
541 {"protect-telnet", &protect_telnet, 0},
542 {"dcc-sanitycheck", &dcc_sanitycheck, 0},
543 {"sort-users", &sort_users, 0},
544 {"ident-timeout", &identtimeout, 0},
545 {"share-unlinks", &share_unlinks, 0},
546 {"log-time", &shtime, 0},
547 {"allow-dk-cmds", &allow_dk_cmds, 0},
548 {"resolve-timeout", &resolve_timeout, 0},
549 {"must-be-owner", &must_be_owner, 1},
550 {"paranoid-telnet-flood", &par_telnet_flood, 0},
551 {"use-exempts", &use_exempts, 0},
552 {"use-invites", &use_invites, 0},
553 {"quiet-save", &quiet_save, 0},
554 {"force-expire", &force_expire, 0},
555 {"dupwait-timeout", &dupwait_timeout, 0},
556 {"strict-host", &strict_host, 0},
557 {"userfile-perm", &userfile_perm, 0},
558 {"copy-to-tmp", &copy_to_tmp, 0},
559 {"quiet-reject", &quiet_reject, 0},
560 {"cidr-support", &cidr_support, 0},
561 {"strict-servernames", &strict_servernames, 0}, /* compat */
562 {"enable-simul", &enable_simul, 0}, /* compat */
563 {"debug-output", &debug_output, 0}, /* compat */
564 {"use-console-r", &use_console_r, 0}, /* compat */
565 #ifdef IPV6
566 {"prefer-ipv6", &pref_af, 0},
567 #endif
568 {NULL, NULL, 0}
569 };
570
571 static tcl_coups def_tcl_coups[] = {
572 {"telnet-flood", &flood_telnet_thr, &flood_telnet_time},
573 {"reserved-portrange", &reserved_port_min, &reserved_port_max},
574 {NULL, NULL, NULL}
575 };
576
577 /* Set up Tcl variables that will hook into eggdrop internal vars via
578 * trace callbacks.
579 */
580 static void init_traces()
581 {
582 add_tcl_coups(def_tcl_coups);
583 add_tcl_strings(def_tcl_strings);
584 add_tcl_ints(def_tcl_ints);
585 }
586
587 void kill_tcl()
588 {
589 rem_tcl_coups(def_tcl_coups);
590 rem_tcl_strings(def_tcl_strings);
591 rem_tcl_ints(def_tcl_ints);
592 kill_bind();
593 Tcl_DeleteInterp(interp);
594 }
595
596 extern tcl_cmds tcluser_cmds[], tcldcc_cmds[], tclmisc_cmds[],
597 tclmisc_objcmds[], tcldns_cmds[];
598
599 #ifdef REPLACE_NOTIFIER
600 /* The tickle_*() functions replace the Tcl Notifier
601 * The tickle_*() functions can be called by Tcl threads
602 */
603 void tickle_SetTimer (TCL_CONST86 Tcl_Time *timePtr)
604 {
605 struct threaddata *td = threaddata();
606 /* we can block 1 second maximum, because we have SECONDLY events */
607 if (!timePtr || timePtr->sec > 1 || (timePtr->sec == 1 && timePtr->usec > 0)) {
608 td->blocktime.tv_sec = 1;
609 td->blocktime.tv_usec = 0;
610 } else {
611 td->blocktime.tv_sec = timePtr->sec;
612 td->blocktime.tv_usec = timePtr->usec;
613 }
614 }
615
616 int tickle_WaitForEvent (TCL_CONST86 Tcl_Time *timePtr)
617 {
618 struct threaddata *td = threaddata();
619
620 tickle_SetTimer(timePtr);
621 return (*td->mainloopfunc)(0);
622 }
623
624 void tickle_CreateFileHandler(int fd, int mask, Tcl_FileProc *proc, ClientData cd)
625 {
626 alloctclsock(fd, mask, proc, cd);
627 }
628
629 void tickle_DeleteFileHandler(int fd)
630 {
631 killtclsock(fd);
632 }
633
634 void tickle_FinalizeNotifier(ClientData cd)
635 {
636 struct threaddata *td = threaddata();
637 if (td->socklist)
638 nfree(td->socklist);
639 }
640
641 ClientData tickle_InitNotifier()
642 {
643 static int ismainthread = 1;
644 init_threaddata(ismainthread);
645 if (ismainthread)
646 ismainthread = 0;
647 return NULL;
648 }
649
650 int tclthreadmainloop(int zero)
651 {
652 int i;
653 i = sockread(NULL, NULL, threaddata()->socklist, threaddata()->MAXSOCKS, 1);
654 return (i == -4);
655 }
656
657 struct threaddata *threaddata()
658 {
659 static Tcl_ThreadDataKey tdkey;
660 struct threaddata *td = Tcl_GetThreadData(&tdkey, sizeof(struct threaddata));
661 return td;
662 }
663
664 #else /* REPLACE_NOTIFIER */
665
666 int tclthreadmainloop() { return 0; }
667
668 struct threaddata *threaddata()
669 {
670 static struct threaddata tsd;
671 return &tsd;
672 }
673
674 #endif /* REPLACE_NOTIFIER */
675
676 int init_threaddata(int mainthread)
677 {
678 struct threaddata *td = threaddata();
679 td->mainloopfunc = mainthread ? mainloop : tclthreadmainloop;
680 td->socklist = NULL;
681 td->mainthread = mainthread;
682 td->blocktime.tv_sec = 1;
683 td->blocktime.tv_usec = 0;
684 td->MAXSOCKS = 0;
685 increase_socks_max();
686 return 0;
687 }
688
689 /* Not going through Tcl's crazy main() system (what on earth was he
690 * smoking?!) so we gotta initialize the Tcl interpreter
691 */
692 void init_tcl(int argc, char **argv)
693 {
694 #ifdef REPLACE_NOTIFIER
695 Tcl_NotifierProcs notifierprocs;
696 #endif /* REPLACE_NOTIFIER */
697
698 #ifdef USE_TCL_ENCODING
699 const char *encoding;
700 int i;
701 char *langEnv;
702 #endif /* USE_TCL_ENCODING */
703 #ifdef USE_TCL_PACKAGE
704 int j;
705 char pver[1024] = "";
706 #endif /* USE_TCL_PACKAGE */
707
708 #ifdef REPLACE_NOTIFIER
709 egg_bzero(&notifierprocs, sizeof(notifierprocs));
710 notifierprocs.initNotifierProc = tickle_InitNotifier;
711 notifierprocs.createFileHandlerProc = tickle_CreateFileHandler;
712 notifierprocs.deleteFileHandlerProc = tickle_DeleteFileHandler;
713 notifierprocs.setTimerProc = tickle_SetTimer;
714 notifierprocs.waitForEventProc = tickle_WaitForEvent;
715 notifierprocs.finalizeNotifierProc = tickle_FinalizeNotifier;
716
717 Tcl_SetNotifier(&notifierprocs);
718 #endif /* REPLACE_NOTIFIER */
719
720 /* This must be done *BEFORE* Tcl_SetSystemEncoding(),
721 * or Tcl_SetSystemEncoding() will cause a segfault.
722 */
723 #ifdef USE_TCL_FINDEXEC
724 /* This is used for 'info nameofexecutable'.
725 * The filename in argv[0] must exist in a directory listed in
726 * the environment variable PATH for it to register anything.
727 */
728 Tcl_FindExecutable(argv[0]);
729 #endif /* USE_TCL_FINDEXEC */
730
731 /* Initialize the interpreter */
732 interp = Tcl_CreateInterp();
733
734 #ifdef DEBUG_MEM
735 /* Initialize Tcl's memory debugging if we want it */
736 Tcl_InitMemory(interp);
737 #endif
738
739 /* Set Tcl variable tcl_interactive to 0 */
740 Tcl_SetVar(interp, "tcl_interactive", "0", TCL_GLOBAL_ONLY);
741
742 /* Setup script library facility */
743 Tcl_Init(interp);
744 Tcl_SetServiceMode(TCL_SERVICE_ALL);
745
746 /* Code based on Tcl's TclpSetInitialEncodings() */
747 #ifdef USE_TCL_ENCODING
748 /* Determine the current encoding from the LC_* or LANG environment
749 * variables.
750 */
751 langEnv = getenv("LC_ALL");
752 if (langEnv == NULL || langEnv[0] == '\0') {
753 langEnv = getenv("LC_CTYPE");
754 }
755 if (langEnv == NULL || langEnv[0] == '\0') {
756 langEnv = getenv("LANG");
757 }
758 if (langEnv == NULL || langEnv[0] == '\0') {
759 langEnv = NULL;
760 }
761
762 encoding = NULL;
763 if (langEnv != NULL) {
764 for (i = 0; localeTable[i].lang != NULL; i++)
765 if (strcmp(localeTable[i].lang, langEnv) == 0) {
766 encoding = localeTable[i].encoding;
767 break;
768 }
769
770 /* There was no mapping in the locale table. If there is an
771 * encoding subfield, we can try to guess from that.
772 */
773 if (encoding == NULL) {
774 char *p;
775
776 for (p = langEnv; *p != '\0'; p++) {
777 if (*p == '.') {
778 p++;
779 break;
780 }
781 }
782 if (*p != '\0') {
783 Tcl_DString ds;
784
785 Tcl_DStringInit(&ds);
786 Tcl_DStringAppend(&ds, p, -1);
787
788 encoding = Tcl_DStringValue(&ds);
789 Tcl_UtfToLower(Tcl_DStringValue(&ds));
790 if (Tcl_SetSystemEncoding(NULL, encoding) == TCL_OK) {
791 Tcl_DStringFree(&ds);
792 goto resetPath;
793 }
794 Tcl_DStringFree(&ds);
795 encoding = NULL;
796 }
797 }
798 }
799
800 if (encoding == NULL) {
801 encoding = "iso8859-1";
802 }
803
804 Tcl_SetSystemEncoding(NULL, encoding);
805
806 resetPath:
807
808 /* Initialize the C library's locale subsystem. */
809 setlocale(LC_CTYPE, "");
810
811 /* In case the initial locale is not "C", ensure that the numeric
812 * processing is done in "C" locale regardless. */
813 setlocale(LC_NUMERIC, "C");
814
815 /* Keep the iso8859-1 encoding preloaded. The IO package uses it for
816 * gets on a binary channel. */
817 Tcl_GetEncoding(NULL, "iso8859-1");
818 #endif /* USE_TCL_ENCODING */
819
820 #ifdef USE_TCL_PACKAGE
821 /* Add eggdrop to Tcl's package list */
822 for (j = 0; j <= strlen(egg_version); j++) {
823 if ((egg_version[j] == ' ') || (egg_version[j] == '+'))
824 break;
825 pver[strlen(pver)] = egg_version[j];
826 }
827 Tcl_PkgProvide(interp, "eggdrop", pver);
828 #endif /* USE_TCL_PACKAGE */
829
830 /* Initialize binds and traces */
831 init_bind();
832 init_traces();
833
834 /* Add new commands */
835 add_tcl_commands(tcluser_cmds);
836 add_tcl_commands(tcldcc_cmds);
837 add_tcl_commands(tclmisc_cmds);
838 #ifdef USE_TCL_OBJ
839 add_tcl_objcommands(tclmisc_objcmds);
840 #endif
841 add_tcl_commands(tcldns_cmds);
842 }
843
844 void do_tcl(char *whatzit, char *script)
845 {
846 int code;
847 char *result;
848 #ifdef USE_TCL_ENCODING
849 Tcl_DString dstr;
850 #endif
851
852 code = Tcl_Eval(interp, script);
853
854 #ifdef USE_TCL_ENCODING
855 /* properly convert string to system encoding. */
856 Tcl_DStringInit(&dstr);
857 Tcl_UtfToExternalDString(NULL, tcl_resultstring(), -1, &dstr);
858 result = Tcl_DStringValue(&dstr);
859 #else
860 /* use old pre-Tcl 8.1 way. */
861 result = tcl_resultstring();
862 #endif
863
864 if (code != TCL_OK) {
865 putlog(LOG_MISC, "*", "Tcl error in script for '%s':", whatzit);
866 putlog(LOG_MISC, "*", "%s", result);
867 }
868
869 #ifdef USE_TCL_ENCODING
870 Tcl_DStringFree(&dstr);
871 #endif
872 }
873
874 /* Interpret tcl file fname.
875 *
876 * returns: 1 - if everything was okay
877 */
878 int readtclprog(char *fname)
879 {
880 int code;
881 EGG_CONST char *result;
882 #ifdef USE_TCL_ENCODING
883 Tcl_DString dstr;
884 #endif
885
886 if (!file_readable(fname))
887 return 0;
888
889 code = Tcl_EvalFile(interp, fname);
890 result = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
891
892 #ifdef USE_TCL_ENCODING
893 /* properly convert string to system encoding. */
894 Tcl_DStringInit(&dstr);
895 Tcl_UtfToExternalDString(NULL, result, -1, &dstr);
896 result = Tcl_DStringValue(&dstr);
897 #endif
898
899 if (code != TCL_OK) {
900 putlog(LOG_MISC, "*", "Tcl error in file '%s':", fname);
901 putlog(LOG_MISC, "*", "%s", result);
902 code = 0; /* JJM: refactored to remove premature return */
903 } else {
904 /* Refresh internal variables */
905 code = 1;
906 }
907
908 #ifdef USE_TCL_ENCODING
909 Tcl_DStringFree(&dstr);
910 #endif
911
912 return code;
913 }
914
915 void add_tcl_strings(tcl_strings *list)
916 {
917 int i;
918 strinfo *st;
919 int tmp;
920
921 for (i = 0; list[i].name; i++) {
922 st = nmalloc(sizeof *st);
923 strtot += sizeof(strinfo);
924 st->max = list[i].length - (list[i].flags & STR_DIR);
925 if (list[i].flags & STR_PROTECT)
926 st->max = -st->max;
927 st->str = list[i].buf;
928 st->flags = (list[i].flags & STR_DIR);
929 tmp = protect_readonly;
930 protect_readonly = 0;
931 tcl_eggstr((ClientData) st, interp, list[i].name, NULL, TCL_TRACE_WRITES);
932 protect_readonly = tmp;
933 tcl_eggstr((ClientData) st, interp, list[i].name, NULL, TCL_TRACE_READS);
934 Tcl_TraceVar(interp, list[i].name, TCL_TRACE_READS | TCL_TRACE_WRITES |
935 TCL_TRACE_UNSETS, tcl_eggstr, (ClientData) st);
936 }
937 }
938
939 void rem_tcl_strings(tcl_strings *list)
940 {
941 int i, f;
942 strinfo *st;
943
944 f = TCL_GLOBAL_ONLY | TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS;
945 for (i = 0; list[i].name; i++) {
946 st = (strinfo *) Tcl_VarTraceInfo(interp, list[i].name, f, tcl_eggstr,
947 NULL);
948 Tcl_UntraceVar(interp, list[i].name, f, tcl_eggstr, st);
949 if (st != NULL) {
950 strtot -= sizeof(strinfo);
951 nfree(st);
952 }
953 }
954 }
955
956 void add_tcl_ints(tcl_ints *list)
957 {
958 int i, tmp;
959 intinfo *ii;
960
961 for (i = 0; list[i].name; i++) {
962 ii = nmalloc(sizeof *ii);
963 strtot += sizeof(intinfo);
964 ii->var = list[i].val;
965 ii->ro = list[i].readonly;
966 tmp = protect_readonly;
967 protect_readonly = 0;
968 tcl_eggint((ClientData) ii, interp, list[i].name, NULL, TCL_TRACE_WRITES);
969 protect_readonly = tmp;
970 tcl_eggint((ClientData) ii, interp, list[i].name, NULL, TCL_TRACE_READS);
971 Tcl_TraceVar(interp, list[i].name,
972 TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
973 tcl_eggint, (ClientData) ii);
974 }
975
976 }
977
978 void rem_tcl_ints(tcl_ints *list)
979 {
980 int i, f;
981 intinfo *ii;
982
983 f = TCL_GLOBAL_ONLY | TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS;
984 for (i = 0; list[i].name; i++) {
985 ii = (intinfo *) Tcl_VarTraceInfo(interp, list[i].name, f, tcl_eggint,
986 NULL);
987 Tcl_UntraceVar(interp, list[i].name, f, tcl_eggint, (ClientData) ii);
988 if (ii) {
989 strtot -= sizeof(intinfo);
990 nfree(ii);
991 }
992 }
993 }
994
995 /* Allocate couplet space for tracing couplets
996 */
997 void add_tcl_coups(tcl_coups *list)
998 {
999 coupletinfo *cp;
1000 int i;
1001
1002 for (i = 0; list[i].name; i++) {
1003 cp = nmalloc(sizeof *cp);
1004 strtot += sizeof(coupletinfo);
1005 cp->left = list[i].lptr;
1006 cp->right = list[i].rptr;
1007 tcl_eggcouplet((ClientData) cp, interp, list[i].name, NULL,
1008 TCL_TRACE_WRITES);
1009 tcl_eggcouplet((ClientData) cp, interp, list[i].name, NULL,
1010 TCL_TRACE_READS);
1011 Tcl_TraceVar(interp, list[i].name,
1012 TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
1013 tcl_eggcouplet, (ClientData) cp);
1014 }
1015 }
1016
1017 void rem_tcl_coups(tcl_coups *list)
1018 {
1019 int i, f;
1020 coupletinfo *cp;
1021
1022 f = TCL_GLOBAL_ONLY | TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS;
1023 for (i = 0; list[i].name; i++) {
1024 cp = (coupletinfo *) Tcl_VarTraceInfo(interp, list[i].name, f,
1025 tcl_eggcouplet, NULL);
1026 strtot -= sizeof(coupletinfo);
1027 Tcl_UntraceVar(interp, list[i].name, f, tcl_eggcouplet, (ClientData) cp);
1028 nfree(cp);
1029 }
1030 }
1031
1032 /* Check if the Tcl library supports threads
1033 */
1034 int tcl_threaded()
1035 {
1036 #ifdef HAVE_TCL_GETCURRENTTHREAD
1037 if (Tcl_GetCurrentThread() != (Tcl_ThreadId)0)
1038 return 1;
1039 #endif
1040
1041 return 0;
1042 }
1043
1044 /* Check if we need to fork before initializing Tcl
1045 */
1046 int fork_before_tcl()
1047 {
1048 #ifndef REPLACE_NOTIFIER
1049 return tcl_threaded();
1050 #endif
1051 return 0;
1052 }

webmaster@eggheads.org
ViewVC Help
Powered by ViewVC 1.1.23