/[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.4 - (show annotations) (download) (as text)
Sun Jun 27 20:37:25 1999 UTC (20 years, 3 months ago) by guppy
Branch: MAIN
Changes since 1.3: +4 -2 lines
File MIME type: text/x-chdr
A whole bunch of drummer patches

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

webmaster@eggheads.org
ViewVC Help
Powered by ViewVC 1.1.23