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

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

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


Revision 1.3 - (show annotations) (download) (as text)
Thu Jun 24 19:54:57 1999 UTC (20 years, 3 months ago) by segfault
Branch: MAIN
Changes since 1.2: +2 -1 lines
File MIME type: text/x-chdr
Patch by Fabian to allow a customizable (by file) telnet banner handled
similarly to motd

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 * dprintf'ized, 4feb1996
8 */
9 /*
10 * This file is part of the eggdrop source code
11 * copyright (c) 1997 Robey Pointer
12 * and is distributed according to the GNU general public license.
13 * For full details, read the top of 'main.c' or the file called
14 * COPYING that was distributed with this code.
15 */
16
17 #include "main.h"
18
19 /* used for read/write to internal strings */
20 typedef struct {
21 char *str; /* pointer to actual string in eggdrop */
22 int max; /* max length (negative: read-only var when protect is on) */
23 /* (0: read-only ALWAYS) */
24 int flags; /* 1 = directory */
25 } strinfo;
26
27 typedef struct {
28 int *var;
29 int ro;
30 } intinfo;
31
32 int protect_readonly = 0; /* turn on/off readonly protection */
33 char whois_fields[121] = ""; /* fields to display in a .whois */
34 Tcl_Interp *interp; /* eggdrop always uses the same interpreter */
35
36 /* 1/2 of these arent even here anymore, one day I'll clean them up */
37 /* done - arthur2 */
38 extern int backgrd, flood_telnet_thr, flood_telnet_time;
39 extern int shtime, share_greet, require_p, keep_all_logs;
40 extern int use_stderr, allow_new_telnets, stealth_telnets;
41 extern int default_flags, conmask, switch_logfiles_at, connect_timeout;
42 extern int firewallport, reserved_port, notify_users_at;
43 extern int flood_thr, ignore_time;
44 extern char origbotname[], botuser[], motdfile[], admin[], userfile[],
45 firewall[], helpdir[], notify_new[], hostname[], myip[], moddir[],
46 tempdir[], owner[], network[], botnetnick[], bannerfile[];
47 extern int die_on_sighup, die_on_sigterm, max_logs, max_logsize, enable_simul;
48 extern int dcc_total, debug_output, identtimeout, protect_telnet;
49 extern int egg_numver, share_unlinks, dcc_sanitycheck, sort_users;
50 extern struct dcc_t *dcc;
51 extern char egg_version[];
52 extern tcl_timer_t *timer, *utimer;
53 extern time_t online_since;
54 extern log_t *logs;
55 extern int tands;
56 extern int resolve_timeout;
57 extern char natip[];
58
59 /* confvar patch by aaronwl */
60 extern char configfile[];
61 int dcc_flood_thr = 3;
62 int debug_tcl = 0;
63 int use_silence = 0;
64 int remote_boots = 2;
65 int allow_dk_cmds = 1;
66 int must_be_owner = 1;
67 int max_dcc = 20; /* needs at least 4 or 5 just to get started
68 * 20 should be enough */
69 int min_dcc_port = 1024; /* dcc-portrange, min port - dw/guppy */
70 int max_dcc_port = 65535; /* dcc-portrange, max port - dw/guppy */
71 int quick_logs = 0; /* quick write logs?
72 * flush em every min instead of every 5 */
73 int par_telnet_flood = 1; /* trigger telnet flood for +f ppl? - dw */
74
75 /* prototypes for tcl */
76 Tcl_Interp *Tcl_CreateInterp();
77 int strtot = 0;
78
79 int expmem_tcl()
80 {
81 int i, tot = 0;
82
83 context;
84 for (i = 0; i < max_logs; i++)
85 if (logs[i].filename != NULL) {
86 tot += strlen(logs[i].filename) + 1;
87 tot += strlen(logs[i].chname) + 1;
88 }
89 return tot + strtot;
90 }
91
92 /***********************************************************************/
93
94 /* logfile [<modes> <channel> <filename>] */
95 static int tcl_logfile STDVAR
96 {
97 int i;
98 char s[151];
99
100 BADARGS(1, 4, " ?logModes channel logFile?");
101 if (argc == 1) {
102 /* they just want a list of the logfiles and modes */
103 for (i = 0; i < max_logs; i++)
104 if (logs[i].filename != NULL) {
105 strcpy(s, masktype(logs[i].mask));
106 strcat(s, " ");
107 strcat(s, logs[i].chname);
108 strcat(s, " ");
109 strcat(s, logs[i].filename);
110 Tcl_AppendElement(interp, s);
111 }
112 return TCL_OK;
113 }
114 BADARGS(4, 4, " ?logModes channel logFile?");
115 for (i = 0; i < max_logs; i++)
116 if ((logs[i].filename != NULL) && (!strcmp(logs[i].filename, argv[3]))) {
117 logs[i].mask = logmodes(argv[1]);
118 nfree(logs[i].chname);
119 logs[i].chname = NULL;
120 if (!logs[i].mask) {
121 /* ending logfile */
122 nfree(logs[i].filename);
123 logs[i].filename = NULL;
124 if (logs[i].f != NULL) {
125 fclose(logs[i].f);
126 logs[i].f = NULL;
127 }
128 } else {
129 logs[i].chname = (char *) nmalloc(strlen(argv[2]) + 1);
130 strcpy(logs[i].chname, argv[2]);
131 }
132 Tcl_AppendResult(interp, argv[3], NULL);
133 return TCL_OK;
134 }
135 for (i = 0; i < max_logs; i++)
136 if (logs[i].filename == NULL) {
137 logs[i].mask = logmodes(argv[1]);
138 logs[i].filename = (char *) nmalloc(strlen(argv[3]) + 1);
139 strcpy(logs[i].filename, argv[3]);
140 logs[i].chname = (char *) nmalloc(strlen(argv[2]) + 1);
141 strcpy(logs[i].chname, argv[2]);
142 Tcl_AppendResult(interp, argv[3], NULL);
143 return TCL_OK;
144 }
145 Tcl_AppendResult(interp, "reached max # of logfiles", NULL);
146 return TCL_ERROR;
147 }
148
149 int findidx(int z)
150 {
151 int j;
152
153 for (j = 0; j < dcc_total; j++)
154 if ((dcc[j].sock == z) && (dcc[j].type->flags & DCT_VALIDIDX))
155 return j;
156 return -1;
157 }
158
159 static void botnet_change(char *new)
160 {
161 if (strcasecmp(botnetnick, new) != 0) {
162 /* trying to change bot's nickname */
163 if (tands > 0) {
164 putlog(LOG_MISC, "*", "* Tried to change my botnet nick, but I'm still linked to a botnet.");
165 putlog(LOG_MISC, "*", "* (Unlink and try again.)");
166 return;
167 } else {
168 if (botnetnick[0])
169 putlog(LOG_MISC, "*", "* IDENTITY CHANGE: %s -> %s", botnetnick, new);
170 strcpy(botnetnick, new);
171 }
172 }
173 }
174
175 /**********************************************************************/
176
177 int init_dcc_max(), init_misc();
178
179 /* used for read/write to integer couplets */
180 typedef struct {
181 int *left; /* left side of couplet */
182 int *right; /* right side */
183 } coupletinfo;
184
185 /* read/write integer couplets (int1:int2) */
186 static char *tcl_eggcouplet(ClientData cdata, Tcl_Interp * irp, char *name1,
187 char *name2, int flags)
188 {
189 char *s, s1[41];
190 coupletinfo *cp = (coupletinfo *) cdata;
191
192 if (flags & (TCL_TRACE_READS | TCL_TRACE_UNSETS)) {
193 sprintf(s1, "%d:%d", *(cp->left), *(cp->right));
194 Tcl_SetVar2(interp, name1, name2, s1, TCL_GLOBAL_ONLY);
195 } else { /* writes */
196 s = Tcl_GetVar2(interp, name1, name2, TCL_GLOBAL_ONLY);
197 if (s != NULL) {
198 int nr1, nr2;
199
200 if (strlen(s) > 40)
201 s[40] = 0;
202 sscanf(s, "%d%*c%d", &nr1, &nr2);
203 *(cp->left) = nr1;
204 *(cp->right) = nr2;
205 }
206 }
207 return NULL;
208 }
209
210 /* read/write normal integer */
211 static char *tcl_eggint(ClientData cdata, Tcl_Interp * irp, char *name1,
212 char *name2, int flags)
213 {
214 char *s, s1[40];
215 long l;
216 intinfo *ii = (intinfo *) cdata;
217
218 if (flags & (TCL_TRACE_READS | TCL_TRACE_UNSETS)) {
219 /* special cases */
220 if ((int *) ii->var == &conmask)
221 strcpy(s1, masktype(conmask));
222 else if ((int *) ii->var == &default_flags) {
223 struct flag_record fr =
224 {FR_GLOBAL, 0, 0, 0, 0, 0};
225 fr.global = default_flags;
226
227 build_flags(s1, &fr, 0);
228 } else
229 sprintf(s1, "%d", *(int *) ii->var);
230 Tcl_SetVar2(interp, name1, name2, s1, TCL_GLOBAL_ONLY);
231 return NULL;
232 } else { /* writes */
233 s = Tcl_GetVar2(interp, name1, name2, TCL_GLOBAL_ONLY);
234 if (s != NULL) {
235 if ((int *) ii->var == &conmask) {
236 if (s[0])
237 conmask = logmodes(s);
238 else
239 conmask = LOG_MODES | LOG_MISC | LOG_CMDS;
240 } else if ((int *) ii->var == &default_flags) {
241 struct flag_record fr =
242 {FR_GLOBAL, 0, 0, 0, 0, 0};
243
244 break_down_flags(s, &fr, 0);
245 default_flags = fr.global;
246 } else if ((ii->ro == 2) || ((ii->ro == 1) && protect_readonly)) {
247 return "read-only variable";
248 } else {
249 if (Tcl_ExprLong(interp, s, &l) == TCL_ERROR)
250 return interp->result;
251 if ((int *) ii->var == &max_dcc) {
252 if (l < max_dcc)
253 return "you can't DECREASE max-dcc";
254 max_dcc = l;
255 init_dcc_max();
256 } else if ((int *) ii->var == &max_logs) {
257 if (l < max_logs)
258 return "you can't DECREASE max-logs";
259 max_logs = l;
260 init_misc();
261 } else
262 *(ii->var) = (int) l;
263 }
264 }
265 return NULL;
266 }
267 }
268
269 /* read/write normal string variable */
270 static char *tcl_eggstr(ClientData cdata, Tcl_Interp * irp, char *name1,
271 char *name2, int flags)
272 {
273 char *s;
274 strinfo *st = (strinfo *) cdata;
275
276 if (flags & (TCL_TRACE_READS | TCL_TRACE_UNSETS)) {
277 if ((st->str == firewall) && (firewall[0])) {
278 char s1[161];
279
280 sprintf(s1, "%s:%d", firewall, firewallport);
281 Tcl_SetVar2(interp, name1, name2, s1, TCL_GLOBAL_ONLY);
282 } else
283 Tcl_SetVar2(interp, name1, name2, st->str, TCL_GLOBAL_ONLY);
284 if (st->max <= 0) {
285 if ((flags & TCL_TRACE_UNSETS) && (protect_readonly || (st->max == 0))) {
286 /* no matter what we do, it won't return the error */
287 Tcl_SetVar2(interp, name1, name2, st->str, TCL_GLOBAL_ONLY);
288 Tcl_TraceVar(interp, name1, TCL_TRACE_READS | TCL_TRACE_WRITES |
289 TCL_TRACE_UNSETS, tcl_eggstr, (ClientData) st);
290 return "read-only variable";
291 }
292 }
293 return NULL;
294 } else { /* writes */
295 if ((st->max <= 0) && (protect_readonly || (st->max == 0))) {
296 Tcl_SetVar2(interp, name1, name2, st->str, TCL_GLOBAL_ONLY);
297 return "read-only variable";
298 }
299 s = Tcl_GetVar2(interp, name1, name2, TCL_GLOBAL_ONLY);
300 if (s != NULL) {
301 if (strlen(s) > abs(st->max))
302 s[abs(st->max)] = 0;
303 if (st->str == botnetnick)
304 botnet_change(s);
305 else if (st->str == firewall) {
306 splitc(firewall, s, ':');
307 if (!firewall[0])
308 strcpy(firewall, s);
309 else
310 firewallport = atoi(s);
311 } else
312 strcpy(st->str, s);
313 if ((st->flags) && (s[0])) {
314 if (st->str[strlen(st->str) - 1] != '/')
315 strcat(st->str, "/");
316 }
317 }
318 return NULL;
319 }
320 }
321
322 /* add/remove tcl commands */
323 void add_tcl_commands(tcl_cmds * tab)
324 {
325 int i;
326
327 for (i = 0; tab[i].name; i++)
328 Tcl_CreateCommand(interp, tab[i].name, tab[i].func, NULL, NULL);
329 }
330
331 void rem_tcl_commands(tcl_cmds * tab)
332 {
333 int i;
334
335 for (i = 0; tab[i].name; i++)
336 Tcl_DeleteCommand(interp, tab[i].name);
337 }
338
339 static tcl_strings def_tcl_strings[] =
340 {
341 {"botnet-nick", botnetnick, HANDLEN, 0},
342 {"userfile", userfile, 120, STR_PROTECT},
343 {"motd", motdfile, 120, STR_PROTECT},
344 {"admin", admin, 120, 0},
345 {"help-path", helpdir, 120, STR_DIR | STR_PROTECT},
346 {"temp-path", tempdir, 120, STR_DIR | STR_PROTECT},
347 #ifndef STATIC
348 {"mod-path", moddir, 120, STR_DIR | STR_PROTECT},
349 #endif
350 {"notify-newusers", notify_new, 120, 0},
351 {"owner", owner, 120, STR_PROTECT},
352 {"my-hostname", hostname, 120, 0},
353 {"my-ip", myip, 120, 0},
354 {"network", network, 40, 0},
355 {"whois-fields", whois_fields, 120, 0},
356 {"nat-ip", natip, 120, 0},
357 {"username", botuser, 10, 0},
358 {"version", egg_version, 0, 0},
359 {"firewall", firewall, 120, 0},
360 /* confvar patch by aaronwl */
361 {"config", configfile, 0, 0},
362 {"telnet-banner", bannerfile, 120, STR_PROTECT},
363 {0, 0, 0, 0}
364 };
365
366 /* ints */
367
368 static tcl_ints def_tcl_ints[] =
369 {
370 {"ignore-time", &ignore_time, 0},
371 {"dcc-flood-thr", &dcc_flood_thr, 0},
372 {"hourly-updates", &notify_users_at, 0},
373 {"switch-logfiles-at", &switch_logfiles_at, 0},
374 {"connect-timeout", &connect_timeout, 0},
375 {"reserved-port", &reserved_port, 0},
376 /* booleans (really just ints) */
377 {"require-p", &require_p, 0},
378 {"keep-all-logs", &keep_all_logs, 0},
379 {"open-telnets", &allow_new_telnets, 0},
380 {"stealth-telnets", &stealth_telnets, 0},
381 {"uptime", (int *) &online_since, 2},
382 {"console", &conmask, 0},
383 {"default-flags", &default_flags, 0},
384 /* moved from eggdrop.h */
385 {"numversion", &egg_numver, 2},
386 {"debug-tcl", &debug_tcl, 1},
387 {"die-on-sighup", &die_on_sighup, 1},
388 {"die-on-sigterm", &die_on_sigterm, 1},
389 {"remote-boots", &remote_boots, 1},
390 {"max-dcc", &max_dcc, 0},
391 {"max-logs", &max_logs, 0},
392 {"max-logsize", &max_logsize, 0},
393 {"quick-logs", &quick_logs, 0},
394 {"enable-simul", &enable_simul, 1},
395 {"debug-output", &debug_output, 1},
396 {"protect-telnet", &protect_telnet, 0},
397 {"dcc-sanitycheck", &dcc_sanitycheck, 0},
398 {"sort-users", &sort_users, 0},
399 {"ident-timeout", &identtimeout, 0},
400 {"share-unlinks", &share_unlinks, 0},
401 {"log-time", &shtime, 0},
402 {"allow-dk-cmds", &allow_dk_cmds, 0},
403 {"resolve-timeout", &resolve_timeout, 0},
404 {"must-be-owner", &must_be_owner, 1},
405 {"use-silence", &use_silence, 0}, /* arthur2 */
406 {"paranoid-telnet-flood", &par_telnet_flood, 0},
407 {0, 0, 0} /* arthur2 */
408 };
409
410 static tcl_coups def_tcl_coups[] =
411 {
412 {"telnet-flood", &flood_telnet_thr, &flood_telnet_time},
413 {"dcc-portrange", &min_dcc_port, &max_dcc_port}, /* dw */
414 {0, 0, 0}
415 };
416
417 /* set up Tcl variables that will hook into eggdrop internal vars via */
418 /* trace callbacks */
419 static void init_traces()
420 {
421 add_tcl_coups(def_tcl_coups);
422 add_tcl_strings(def_tcl_strings);
423 add_tcl_ints(def_tcl_ints);
424 }
425
426 void kill_tcl()
427 {
428 rem_tcl_coups(def_tcl_coups);
429 rem_tcl_strings(def_tcl_strings);
430 rem_tcl_ints(def_tcl_ints);
431 kill_bind();
432 Tcl_DeleteInterp(interp);
433 }
434
435 extern tcl_cmds tcluser_cmds[], tcldcc_cmds[], tclmisc_cmds[];
436
437 /* not going through Tcl's crazy main() system (what on earth was he
438 * smoking?!) so we gotta initialize the Tcl interpreter */
439 void init_tcl()
440 {
441 char pver[25];
442
443 /* initialize the interpreter */
444 context;
445 interp = Tcl_CreateInterp();
446 Tcl_Init(interp);
447 init_bind();
448 init_traces();
449 /* add new commands */
450 /* isnt this much neater :) */
451 add_tcl_commands(tcluser_cmds);
452 add_tcl_commands(tcldcc_cmds);
453 add_tcl_commands(tclmisc_cmds);
454
455 #define Q(A,B) Tcl_CreateCommand(interp,A,B,NULL,NULL)
456 Q("logfile", tcl_logfile);
457 sscanf(egg_version, "%s", pver);
458 Tcl_PkgProvide(interp, "eggdrop", pver);
459 }
460
461 /**********************************************************************/
462
463 void do_tcl(char *whatzit, char *script)
464 {
465 int code;
466 FILE *f = 0;
467
468 if (debug_tcl) {
469 f = fopen("DEBUG.TCL", "a");
470 if (f != NULL)
471 fprintf(f, "eval: %s\n", script);
472 }
473 set_tcl_vars();
474 context;
475 code = Tcl_Eval(interp, script);
476 if (debug_tcl && (f != NULL)) {
477 fprintf(f, "done eval, result=%d\n", code);
478 fclose(f);
479 }
480 if (code != TCL_OK) {
481 putlog(LOG_MISC, "*", "Tcl error in script for '%s':", whatzit);
482 putlog(LOG_MISC, "*", "%s", interp->result);
483 }
484 }
485
486 /* read and interpret the configfile given */
487 /* return 1 if everything was okay */
488 int readtclprog(char *fname)
489 {
490 int code;
491 FILE *f;
492
493 set_tcl_vars();
494 f = fopen(fname, "r");
495 if (f == NULL)
496 return 0;
497 fclose(f);
498 if (debug_tcl) {
499 f = fopen("DEBUG.TCL", "a");
500 if (f != NULL) {
501 fprintf(f, "Sourcing file %s ...\n", fname);
502 fclose(f);
503 }
504 }
505 code = Tcl_EvalFile(interp, fname);
506 if (code != TCL_OK) {
507 if (use_stderr) {
508 dprintf(DP_STDERR, "Tcl error in file '%s':\n", fname);
509 dprintf(DP_STDERR, "%s\n",
510 Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY));
511 } else {
512 putlog(LOG_MISC, "*", "Tcl error in file '%s':", fname);
513 putlog(LOG_MISC, "*", "%s\n",
514 Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY));
515 }
516 /* try to go on anyway (shrug) */
517 /* no dont it's to risky now */
518 return 0;
519 }
520 /* refresh internal variables */
521 return 1;
522 }
523
524 void add_tcl_strings(tcl_strings * list)
525 {
526 int i;
527 strinfo *st;
528
529 for (i = 0; list[i].name; i++) {
530 if (list[i].length > 0) {
531 char *p = Tcl_GetVar(interp, list[i].name, TCL_GLOBAL_ONLY);
532
533 if (p != NULL) {
534 strncpy(list[i].buf, p, list[i].length);
535 list[i].buf[list[i].length] = 0;
536 if (list[i].flags & STR_DIR) {
537 int x = strlen(list[i].buf);
538
539 if ((x > 0) && (x < (list[i].length - 1)) &&
540 (list[i].buf[x - 1] != '/')) {
541 list[i].buf[x++] = '/';
542 list[i].buf[x] = 0;
543 }
544 }
545 }
546 }
547 st = (strinfo *) nmalloc(sizeof(strinfo));
548 strtot += sizeof(strinfo);
549 st->max = list[i].length - (list[i].flags & STR_DIR);
550 if (list[i].flags & STR_PROTECT)
551 st->max = -st->max;
552 st->str = list[i].buf;
553 st->flags = (list[i].flags & STR_DIR);
554 Tcl_TraceVar(interp, list[i].name, TCL_TRACE_READS | TCL_TRACE_WRITES |
555 TCL_TRACE_UNSETS, tcl_eggstr, (ClientData) st);
556 }
557 }
558
559 void rem_tcl_strings(tcl_strings * list)
560 {
561 int i;
562 strinfo *st;
563
564 for (i = 0; list[i].name; i++) {
565 st = (strinfo *) Tcl_VarTraceInfo(interp, list[i].name,
566 TCL_TRACE_READS |
567 TCL_TRACE_WRITES |
568 TCL_TRACE_UNSETS,
569 tcl_eggstr, NULL);
570 Tcl_UntraceVar(interp, list[i].name,
571 TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
572 tcl_eggstr, st);
573 if (st != NULL) {
574 strtot -= sizeof(strinfo);
575 nfree(st);
576 }
577 }
578 }
579
580 void add_tcl_ints(tcl_ints * list)
581 {
582 int i;
583 intinfo *ii;
584
585 for (i = 0; list[i].name; i++) {
586 char *p = Tcl_GetVar(interp, list[i].name, TCL_GLOBAL_ONLY);
587
588 if (p != NULL)
589 *(list[i].val) = atoi(p);
590 ii = nmalloc(sizeof(intinfo));
591 strtot += sizeof(intinfo);
592 ii->var = list[i].val;
593 ii->ro = list[i].readonly;
594 Tcl_TraceVar(interp, list[i].name,
595 TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
596 tcl_eggint, (ClientData) ii);
597 }
598
599 }
600
601 void rem_tcl_ints(tcl_ints * list)
602 {
603 int i;
604 intinfo *ii;
605
606 for (i = 0; list[i].name; i++) {
607 ii = (intinfo *) Tcl_VarTraceInfo(interp, list[i].name,
608 TCL_TRACE_READS |
609 TCL_TRACE_WRITES |
610 TCL_TRACE_UNSETS,
611 tcl_eggint, NULL);
612 Tcl_UntraceVar(interp, list[i].name,
613 TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
614 tcl_eggint, (ClientData) ii);
615 if (ii) {
616 strtot -= sizeof(intinfo);
617 nfree(ii);
618 }
619 }
620 }
621
622 /* allocate couplet space for tracing couplets */
623 void add_tcl_coups(tcl_coups * list)
624 {
625 coupletinfo *cp;
626 int i;
627
628 for (i = 0; list[i].name; i++) {
629 cp = (coupletinfo *) nmalloc(sizeof(coupletinfo));
630 strtot += sizeof(coupletinfo);
631 cp->left = list[i].lptr;
632 cp->right = list[i].rptr;
633 Tcl_TraceVar(interp, list[i].name,
634 TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
635 tcl_eggcouplet, (ClientData) cp);
636 }
637 }
638
639 void rem_tcl_coups(tcl_coups * list)
640 {
641 coupletinfo *cp;
642 int i;
643
644 for (i = 0; list[i].name; i++) {
645 cp = (coupletinfo *) Tcl_VarTraceInfo(interp, list[i].name,
646 TCL_TRACE_READS |
647 TCL_TRACE_WRITES |
648 TCL_TRACE_UNSETS,
649 tcl_eggcouplet, NULL);
650 strtot -= sizeof(coupletinfo);
651 Tcl_UntraceVar(interp, list[i].name,
652 TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
653 tcl_eggcouplet, (ClientData) cp);
654 nfree(cp);
655 }
656 }

webmaster@eggheads.org
ViewVC Help
Powered by ViewVC 1.1.23