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

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

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


Revision 1.3 - (hide annotations) (download) (as text)
Mon Aug 23 21:27:40 2010 UTC (8 years, 11 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 simple 1.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 pseudo 1.3 * $Id: tcl.c,v 1.2 2010/08/05 18:12:05 pseudo Exp $
8 simple 1.1 */
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 pseudo 1.2 firewall[], helpdir[], notify_new[], vhost[], moddir[],
50 simple 1.1 tempdir[], owner[], network[], botnetnick[], bannerfile[],
51     egg_version[], natip[], configfile[], logfile_suffix[], log_ts[],
52 pseudo 1.2 textdir[], pid_file[], listen_ip[];
53    
54 simple 1.1
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 pseudo 1.2 #ifdef IPV6
66     extern char vhost6[];
67     extern int pref_af;
68     #endif
69    
70 simple 1.1 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 pseudo 1.2 {"vhost4", vhost, 120, 0},
498     #ifdef IPV6
499     {"vhost6", vhost6, 120, 0},
500     #endif
501     {"listen-addr", listen_ip, 120, 0},
502 simple 1.1 {"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 pseudo 1.2 #ifdef IPV6
566 pseudo 1.3 {"prefer-ipv6", &pref_af, 0},
567 pseudo 1.2 #endif
568 simple 1.1 {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