Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libtksh/tcl/tclInterp.c
1810 views
1
/*
2
* tclInterp.c --
3
*
4
* This file implements the "interp" command which allows creation
5
* and manipulation of Tcl interpreters from within Tcl scripts.
6
*
7
* Copyright (c) 1995 Sun Microsystems, Inc.
8
*
9
* See the file "license.terms" for information on usage and redistribution
10
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11
*
12
* SCCS: @(#) tclInterp.c 1.79 96/09/20 17:20:16
13
*/
14
15
#include <ast.h>
16
#include <stdio.h>
17
#include "tclInt.h"
18
#include "tclPort.h"
19
20
/*
21
* Counter for how many aliases were created (global)
22
*/
23
24
static int aliasCounter = 0;
25
26
/*
27
*
28
* struct Slave:
29
*
30
* Used by the "interp" command to record and find information about slave
31
* interpreters. Maps from a command name in the master to information about
32
* a slave interpreter, e.g. what aliases are defined in it.
33
*/
34
35
typedef struct {
36
Tcl_Interp *masterInterp; /* Master interpreter for this slave. */
37
Tcl_HashEntry *slaveEntry; /* Hash entry in masters slave table for
38
* this slave interpreter. Used to find
39
* this record, and used when deleting the
40
* slave interpreter to delete it from the
41
* masters table. */
42
Tcl_Interp *slaveInterp; /* The slave interpreter. */
43
Tcl_Command interpCmd; /* Interpreter object command. */
44
Tcl_HashTable aliasTable; /* Table which maps from names of commands
45
* in slave interpreter to struct Alias
46
* defined below. */
47
} Slave;
48
49
/*
50
* struct Alias:
51
*
52
* Stores information about an alias. Is stored in the slave interpreter
53
* and used by the source command to find the target command in the master
54
* when the source command is invoked.
55
*/
56
57
typedef struct {
58
char *aliasName; /* Name of alias command. */
59
char *targetName; /* Name of target command in master interp. */
60
Tcl_Interp *targetInterp; /* Master interpreter. */
61
int argc; /* Count of additional args to pass. */
62
char **argv; /* Actual additional args to pass. */
63
Tcl_HashEntry *aliasEntry; /* Entry for the alias hash table in slave.
64
* This is used by alias deletion to remove
65
* the alias from the slave interpreter
66
* alias table. */
67
Tcl_HashEntry *targetEntry; /* Entry for target command in master.
68
* This is used in the master interpreter to
69
* map back from the target command to aliases
70
* redirecting to it. Random access to this
71
* hash table is never required - we are using
72
* a hash table only for convenience. */
73
Tcl_Command slaveCmd; /* Source command in slave interpreter. */
74
} Alias;
75
76
/*
77
* struct Target:
78
*
79
* Maps from master interpreter commands back to the source commands in slave
80
* interpreters. This is needed because aliases can be created between sibling
81
* interpreters and must be deleted when the target interpreter is deleted. In
82
* case they would not be deleted the source interpreter would be left with a
83
* "dangling pointer". One such record is stored in the Master record of the
84
* master interpreter (in the targetTable hashtable, see below) with the
85
* master for each alias which directs to a command in the master. These
86
* records are used to remove the source command for an from a slave if/when
87
* the master is deleted.
88
*/
89
90
typedef struct {
91
Tcl_Command slaveCmd; /* Command for alias in slave interp. */
92
Tcl_Interp *slaveInterp; /* Slave Interpreter. */
93
} Target;
94
95
/*
96
* struct Master:
97
*
98
* This record is used for three purposes: First, slaveTable (a hashtable)
99
* maps from names of commands to slave interpreters. This hashtable is
100
* used to store information about slave interpreters of this interpreter,
101
* to map over all slaves, etc. The second purpose is to store information
102
* about all aliases in slaves (or siblings) which direct to target commands
103
* in this interpreter (using the targetTable hashtable). The third field in
104
* the record, isSafe, denotes whether the interpreter is safe or not. Safe
105
* interpreters have restricted functionality, can only create safe slave
106
* interpreters and can only load safe extensions.
107
*/
108
109
typedef struct {
110
Tcl_HashTable slaveTable; /* Hash table for slave interpreters.
111
* Maps from command names to Slave records. */
112
int isSafe; /* Am I a "safe" interpreter? */
113
Tcl_HashTable targetTable; /* Hash table for Target Records. Contains
114
* all Target records which denote aliases
115
* from slaves or sibling interpreters that
116
* direct to commands in this interpreter. This
117
* table is used to remove dangling pointers
118
* from the slave (or sibling) interpreters
119
* when this interpreter is deleted. */
120
} Master;
121
122
/*
123
* Prototypes for local static procedures:
124
*/
125
126
static int AliasCmd _ANSI_ARGS_((ClientData dummy,
127
Tcl_Interp *currentInterp, int argc, char **argv));
128
static void AliasCmdDeleteProc _ANSI_ARGS_((
129
ClientData clientData));
130
static int AliasHelper _ANSI_ARGS_((Tcl_Interp *curInterp,
131
Tcl_Interp *slaveInterp, Tcl_Interp *masterInterp,
132
Master *masterPtr, char *aliasName,
133
char *targetName, int argc, char **argv));
134
static int CreateInterpObject _ANSI_ARGS_((Tcl_Interp *interp,
135
int argc, char **argv));
136
static Tcl_Interp *CreateSlave _ANSI_ARGS_((Tcl_Interp *interp,
137
char *slavePath, int safe));
138
static int DeleteAlias _ANSI_ARGS_((Tcl_Interp *interp,
139
Tcl_Interp *slaveInterp, char *aliasName));
140
static int DescribeAlias _ANSI_ARGS_((Tcl_Interp *interp,
141
Tcl_Interp *slaveInterp, char *aliasName));
142
static int DeleteInterpObject _ANSI_ARGS_((Tcl_Interp *interp,
143
int argc, char **argv));
144
static int DeleteOneInterpObject _ANSI_ARGS_((Tcl_Interp *interp,
145
char *path));
146
static Tcl_Interp *GetInterp _ANSI_ARGS_((Tcl_Interp *interp,
147
Master *masterPtr, char *path,
148
Master **masterPtrPtr));
149
static int GetTarget _ANSI_ARGS_((Tcl_Interp *interp, char *path,
150
char *aliasName));
151
static void MasterRecordDeleteProc _ANSI_ARGS_((
152
ClientData clientData, Tcl_Interp *interp));
153
static int MakeSafe _ANSI_ARGS_((Tcl_Interp *interp));
154
static int SlaveAliasHelper _ANSI_ARGS_((Tcl_Interp *interp,
155
int argc, char **argv));
156
static int SlaveObjectCmd _ANSI_ARGS_((ClientData dummy,
157
Tcl_Interp *interp, int argc, char **argv));
158
static void SlaveObjectDeleteProc _ANSI_ARGS_((
159
ClientData clientData));
160
static void SlaveRecordDeleteProc _ANSI_ARGS_((
161
ClientData clientData, Tcl_Interp *interp));
162
163
/*
164
* These are all the Tcl core commands which are available in a safe
165
* interpeter:
166
*/
167
168
static char *TclCommandsToKeep[] = {
169
"after", "append", "array",
170
"break",
171
"case", "catch", "clock", "close", "concat", "continue",
172
"eof", "error", "eval", "expr",
173
"fblocked", "fileevent", "flush", "for", "foreach", "format",
174
"gets", "global",
175
"history",
176
"if", "incr", "info", "interp",
177
"join",
178
"lappend", "lindex", "linsert", "list", "llength",
179
"lower", "lrange", "lreplace", "lsearch", "lsort",
180
"package", "pid", "proc", "puts",
181
"read", "regexp", "regsub", "rename", "return",
182
"scan", "seek", "set", "split", "string", "subst", "switch",
183
"tell", "time", "trace",
184
"unset", "unsupported0", "update", "uplevel", "upvar",
185
"vwait",
186
"while",
187
NULL};
188
static int TclCommandsToKeepCt =
189
(sizeof (TclCommandsToKeep) / sizeof (char *)) -1 ;
190
191
/*
192
*----------------------------------------------------------------------
193
*
194
* TclPreventAliasLoop --
195
*
196
* When defining an alias or renaming a command, prevent an alias
197
* loop from being formed.
198
*
199
* Results:
200
* A standard Tcl result.
201
*
202
* Side effects:
203
* If TCL_ERROR is returned, the function also sets interp->result
204
* to an error message.
205
*
206
* NOTE:
207
* This function is public internal (instead of being static to
208
* this file) because it is also used from Tcl_RenameCmd.
209
*
210
*----------------------------------------------------------------------
211
*/
212
213
int
214
TclPreventAliasLoop(interp, cmdInterp, cmdName, proc, clientData)
215
Tcl_Interp *interp; /* Interp in which to report errors. */
216
Tcl_Interp *cmdInterp; /* Interp in which the command is
217
* being defined. */
218
char *cmdName; /* Name of Tcl command we are
219
* attempting to define. */
220
Tcl_CmdProc *proc; /* The command procedure for the
221
* command being created. */
222
ClientData clientData; /* The client data associated with the
223
* command to be created. */
224
{
225
Alias *aliasPtr, *nextAliasPtr;
226
Tcl_CmdInfo cmdInfo;
227
228
/*
229
* If we are not creating or renaming an alias, then it is
230
* always OK to create or rename the command.
231
*/
232
233
if (proc != AliasCmd) {
234
return TCL_OK;
235
}
236
237
/*
238
* OK, we are dealing with an alias, so traverse the chain of aliases.
239
* If we encounter the alias we are defining (or renaming to) any in
240
* the chain then we have a loop.
241
*/
242
243
aliasPtr = (Alias *) clientData;
244
nextAliasPtr = aliasPtr;
245
while (1) {
246
247
/*
248
* If the target of the next alias in the chain is the same as the
249
* source alias, we have a loop.
250
*/
251
252
if ((strcmp(nextAliasPtr->targetName, cmdName) == 0) &&
253
(nextAliasPtr->targetInterp == cmdInterp)) {
254
Tcl_AppendResult(interp, "cannot define or rename alias \"",
255
aliasPtr->aliasName, "\": would create a loop",
256
(char *) NULL);
257
return TCL_ERROR;
258
}
259
260
/*
261
* Otherwise, follow the chain one step further. If the target
262
* command is undefined then there is no loop.
263
*/
264
265
if (Tcl_GetCommandInfo(nextAliasPtr->targetInterp,
266
nextAliasPtr->targetName, &cmdInfo) == 0) {
267
return TCL_OK;
268
}
269
270
/*
271
* See if the target command is an alias - if so, follow the
272
* loop to its target command. Otherwise we do not have a loop.
273
*/
274
275
if (cmdInfo.proc != AliasCmd) {
276
return TCL_OK;
277
}
278
nextAliasPtr = (Alias *) cmdInfo.clientData;
279
}
280
281
/* NOTREACHED */
282
}
283
284
/*
285
*----------------------------------------------------------------------
286
*
287
* MakeSafe --
288
*
289
* Makes its argument interpreter contain only functionality that is
290
* defined to be part of Safe Tcl.
291
*
292
* Results:
293
* None.
294
*
295
* Side effects:
296
* Removes commands from its argument interpreter.
297
*
298
*----------------------------------------------------------------------
299
*/
300
301
static int
302
MakeSafe(interp)
303
Tcl_Interp *interp; /* Interpreter to be made safe. */
304
{
305
char **argv; /* Args for Tcl_Eval. */
306
int argc, keep, i, j; /* Loop indices. */
307
char *cmdGetGlobalCmds = "info commands"; /* What command to run. */
308
char *cmdNoEnv = "unset env"; /* How to get rid of env. */
309
Master *masterPtr; /* Master record of interp
310
* to be made safe. */
311
Tcl_Channel chan; /* Channel to remove from
312
* safe interpreter. */
313
314
/*
315
* Below, Tcl_Eval sets interp->result, so we do not.
316
*/
317
318
Tcl_ResetResult(interp);
319
if ((Tcl_Eval(interp, cmdGetGlobalCmds) == TCL_ERROR) ||
320
(Tcl_SplitList(interp, interp->result, &argc, &argv) != TCL_OK)) {
321
return TCL_ERROR;
322
}
323
for (i = 0; i < argc; i++) {
324
for (keep = 0, j = 0; j < TclCommandsToKeepCt; j++) {
325
if (strcmp(TclCommandsToKeep[j], argv[i]) == 0) {
326
keep = 1;
327
break;
328
}
329
}
330
if (keep == 0) {
331
(void) Tcl_DeleteCommand(interp, argv[i]);
332
}
333
}
334
ckfree((char *) argv);
335
masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord",
336
NULL);
337
if (masterPtr == (Master *) NULL) {
338
panic("MakeSafe: could not find master record");
339
}
340
masterPtr->isSafe = 1;
341
if (Tcl_Eval(interp, cmdNoEnv) == TCL_ERROR) {
342
return TCL_ERROR;
343
}
344
345
/*
346
* Remove the standard channels from the interpreter; safe interpreters
347
* do not ordinarily have access to stdin, stdout and stderr.
348
*
349
* NOTE: These channels are not added to the interpreter by the
350
* Tcl_CreateInterp call, but may be added later, by another I/O
351
* operation. We want to ensure that the interpreter does not have
352
* these channels even if it is being made safe after being used for
353
* some time..
354
*/
355
356
chan = Tcl_GetStdChannel(TCL_STDIN);
357
if (chan != (Tcl_Channel) NULL) {
358
Tcl_UnregisterChannel(interp, chan);
359
}
360
chan = Tcl_GetStdChannel(TCL_STDOUT);
361
if (chan != (Tcl_Channel) NULL) {
362
Tcl_UnregisterChannel(interp, chan);
363
}
364
chan = Tcl_GetStdChannel(TCL_STDERR);
365
if (chan != (Tcl_Channel) NULL) {
366
Tcl_UnregisterChannel(interp, chan);
367
}
368
369
return TCL_OK;
370
}
371
372
/*
373
*----------------------------------------------------------------------
374
*
375
* GetInterp --
376
*
377
* Helper function to find a slave interpreter given a pathname.
378
*
379
* Results:
380
* Returns the slave interpreter known by that name in the calling
381
* interpreter, or NULL if no interpreter known by that name exists.
382
*
383
* Side effects:
384
* Assigns to the pointer variable passed in, if not NULL.
385
*
386
*----------------------------------------------------------------------
387
*/
388
389
static Tcl_Interp *
390
GetInterp(interp, masterPtr, path, masterPtrPtr)
391
Tcl_Interp *interp; /* Interp. to start search from. */
392
Master *masterPtr; /* Its master record. */
393
char *path; /* The path (name) of interp. to be found. */
394
Master **masterPtrPtr; /* (Return) its master record. */
395
{
396
Tcl_HashEntry *hPtr; /* Search element. */
397
Slave *slavePtr; /* Interim slave record. */
398
char **argv; /* Split-up path (name) for interp to find. */
399
int argc, i; /* Loop indices. */
400
Tcl_Interp *searchInterp; /* Interim storage for interp. to find. */
401
402
if (masterPtrPtr != (Master **) NULL) *masterPtrPtr = masterPtr;
403
404
if (Tcl_SplitList(interp, path, &argc, &argv) != TCL_OK) {
405
return (Tcl_Interp *) NULL;
406
}
407
408
for (searchInterp = interp, i = 0; i < argc; i++) {
409
410
hPtr = Tcl_FindHashEntry(&(masterPtr->slaveTable), argv[i]);
411
if (hPtr == (Tcl_HashEntry *) NULL) {
412
ckfree((char *) argv);
413
return (Tcl_Interp *) NULL;
414
}
415
slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
416
searchInterp = slavePtr->slaveInterp;
417
if (searchInterp == (Tcl_Interp *) NULL) {
418
ckfree((char *) argv);
419
return (Tcl_Interp *) NULL;
420
}
421
masterPtr = (Master *) Tcl_GetAssocData(searchInterp,
422
"tclMasterRecord", NULL);
423
if (masterPtrPtr != (Master **) NULL) *masterPtrPtr = masterPtr;
424
if (masterPtr == (Master *) NULL) {
425
ckfree((char *) argv);
426
return (Tcl_Interp *) NULL;
427
}
428
}
429
ckfree((char *) argv);
430
return searchInterp;
431
}
432
433
/*
434
*----------------------------------------------------------------------
435
*
436
* CreateSlave --
437
*
438
* Helper function to do the actual work of creating a slave interp
439
* and new object command. Also optionally makes the new slave
440
* interpreter "safe".
441
*
442
* Results:
443
* Returns the new Tcl_Interp * if successful or NULL if not. If failed,
444
* the result of the invoking interpreter contains an error message.
445
*
446
* Side effects:
447
* Creates a new slave interpreter and a new object command.
448
*
449
*----------------------------------------------------------------------
450
*/
451
452
static Tcl_Interp *
453
CreateSlave(interp, slavePath, safe)
454
Tcl_Interp *interp; /* Interp. to start search from. */
455
char *slavePath; /* Path (name) of slave to create. */
456
int safe; /* Should we make it "safe"? */
457
{
458
Master *masterPtr; /* Master record. */
459
Tcl_Interp *slaveInterp; /* Ptr to slave interpreter. */
460
Tcl_Interp *masterInterp; /* Ptr to master interp for slave. */
461
Slave *slavePtr; /* Slave record. */
462
Tcl_HashEntry *hPtr; /* Entry into interp hashtable. */
463
int new; /* Indicates whether new entry. */
464
int argc; /* Count of elements in slavePath. */
465
char **argv; /* Elements in slavePath. */
466
char *masterPath; /* Path to its master. */
467
468
masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord",
469
NULL);
470
if (masterPtr == (Master *) NULL) {
471
panic("CreatSlave: could not find master record");
472
}
473
474
if (Tcl_SplitList(interp, slavePath, &argc, &argv) != TCL_OK) {
475
return (Tcl_Interp *) NULL;
476
}
477
478
if (argc < 2) {
479
masterInterp = interp;
480
if (argc == 1) {
481
slavePath = argv[0];
482
}
483
} else {
484
masterPath = Tcl_Merge(argc-1, argv);
485
masterInterp = GetInterp(interp, masterPtr, masterPath, &masterPtr);
486
if (masterInterp == (Tcl_Interp *) NULL) {
487
Tcl_AppendResult(interp, "interpreter named \"", masterPath,
488
"\" not found", (char *) NULL);
489
ckfree((char *) argv);
490
ckfree((char *) masterPath);
491
return (Tcl_Interp *) NULL;
492
}
493
ckfree((char *) masterPath);
494
slavePath = argv[argc-1];
495
if (!safe) {
496
safe = masterPtr->isSafe;
497
}
498
}
499
hPtr = Tcl_CreateHashEntry(&(masterPtr->slaveTable), slavePath, &new);
500
if (new == 0) {
501
Tcl_AppendResult(interp, "interpreter named \"", slavePath,
502
"\" already exists, cannot create", (char *) NULL);
503
ckfree((char *) argv);
504
return (Tcl_Interp *) NULL;
505
}
506
slaveInterp = Tcl_CreateInterp();
507
if (slaveInterp == (Tcl_Interp *) NULL) {
508
panic("CreateSlave: out of memory while creating a new interpreter");
509
}
510
slavePtr = (Slave *) ckalloc((unsigned) sizeof(Slave));
511
slavePtr->masterInterp = masterInterp;
512
slavePtr->slaveEntry = hPtr;
513
slavePtr->slaveInterp = slaveInterp;
514
slavePtr->interpCmd = Tcl_CreateCommand(masterInterp, slavePath,
515
SlaveObjectCmd, (ClientData) slaveInterp, SlaveObjectDeleteProc);
516
Tcl_InitHashTable(&(slavePtr->aliasTable), TCL_STRING_KEYS);
517
(void) Tcl_SetAssocData(slaveInterp, "tclSlaveRecord",
518
SlaveRecordDeleteProc, (ClientData) slavePtr);
519
Tcl_SetHashValue(hPtr, (ClientData) slavePtr);
520
Tcl_SetVar(slaveInterp, "tcl_interactive", "0", TCL_GLOBAL_ONLY);
521
522
if (((safe) && (MakeSafe(slaveInterp) == TCL_ERROR)) ||
523
((!safe) && (Tcl_Init(slaveInterp) == TCL_ERROR))) {
524
Tcl_ResetResult(interp);
525
Tcl_AddErrorInfo(interp, Tcl_GetVar2(slaveInterp, "errorInfo", (char *)
526
NULL, TCL_GLOBAL_ONLY));
527
Tcl_SetVar2(interp, "errorCode", (char *) NULL,
528
Tcl_GetVar2(slaveInterp, "errorCode", (char *) NULL,
529
TCL_GLOBAL_ONLY),
530
TCL_GLOBAL_ONLY);
531
if (slaveInterp->freeProc != NULL) {
532
interp->result = slaveInterp->result;
533
interp->freeProc = slaveInterp->freeProc;
534
slaveInterp->freeProc = 0;
535
} else {
536
Tcl_SetResult(interp, slaveInterp->result, TCL_VOLATILE);
537
}
538
Tcl_ResetResult(slaveInterp);
539
(void) Tcl_DeleteCommand(masterInterp, slavePath);
540
slaveInterp = (Tcl_Interp *) NULL;
541
}
542
ckfree((char *) argv);
543
return slaveInterp;
544
}
545
546
/*
547
*----------------------------------------------------------------------
548
*
549
* CreateInterpObject -
550
*
551
* Helper function to do the actual work of creating a new interpreter
552
* and an object command.
553
*
554
* Results:
555
* A Tcl result.
556
*
557
* Side effects:
558
* See user documentation for details.
559
*
560
*----------------------------------------------------------------------
561
*/
562
563
static int
564
CreateInterpObject(interp, argc, argv)
565
Tcl_Interp *interp; /* Invoking interpreter. */
566
int argc; /* Number of arguments. */
567
char **argv; /* Argument strings. */
568
{
569
int safe; /* Create a safe interpreter? */
570
Master *masterPtr; /* Master record. */
571
int moreFlags; /* Expecting more flag args? */
572
char *slavePath; /* Name of slave. */
573
char localSlaveName[200]; /* Local area for creating names. */
574
int i; /* Loop counter. */
575
size_t len; /* Length of option argument. */
576
static int interpCounter = 0; /* Unique id for created names. */
577
578
masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord", NULL);
579
if (masterPtr == (Master *) NULL) {
580
panic("CreateInterpObject: could not find master record");
581
}
582
moreFlags = 1;
583
slavePath = NULL;
584
safe = masterPtr->isSafe;
585
586
if (argc < 2 || argc > 5) {
587
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
588
" create ?-safe? ?--? ?path?\"", (char *) NULL);
589
return TCL_ERROR;
590
}
591
for (i = 2; i < argc; i++) {
592
len = strlen(argv[i]);
593
if ((argv[i][0] == '-') && (moreFlags != 0)) {
594
if ((argv[i][1] == 's') && (strncmp(argv[i], "-safe", len) == 0)
595
&& (len > 1)){
596
safe = 1;
597
} else if ((strncmp(argv[i], "--", len) == 0) && (len > 1)) {
598
moreFlags = 0;
599
} else {
600
Tcl_AppendResult(interp, "bad option \"", argv[i],
601
"\": should be -safe", (char *) NULL);
602
return TCL_ERROR;
603
}
604
} else {
605
slavePath = argv[i];
606
}
607
}
608
if (slavePath == (char *) NULL) {
609
sprintf(localSlaveName, "interp%d", interpCounter);
610
interpCounter++;
611
slavePath = localSlaveName;
612
}
613
if (CreateSlave(interp, slavePath, safe) != NULL) {
614
Tcl_AppendResult(interp, slavePath, (char *) NULL);
615
return TCL_OK;
616
} else {
617
/*
618
* CreateSlave already set interp->result if there was an error,
619
* so we do not do it here.
620
*/
621
return TCL_ERROR;
622
}
623
}
624
625
/*
626
*----------------------------------------------------------------------
627
*
628
* DeleteOneInterpObject --
629
*
630
* Helper function for DeleteInterpObject. It deals with deleting one
631
* interpreter at a time.
632
*
633
* Results:
634
* A standard Tcl result.
635
*
636
* Side effects:
637
* Deletes an interpreter and its interpreter object command.
638
*
639
*----------------------------------------------------------------------
640
*/
641
642
static int
643
DeleteOneInterpObject(interp, path)
644
Tcl_Interp *interp; /* Interpreter for reporting errors. */
645
char *path; /* Path of interpreter to delete. */
646
{
647
Master *masterPtr; /* Interim storage for master record.*/
648
Slave *slavePtr; /* Interim storage for slave record. */
649
Tcl_Interp *masterInterp; /* Master of interp. to delete. */
650
Tcl_HashEntry *hPtr; /* Search element. */
651
int localArgc; /* Local copy of count of elements in
652
* path (name) of interp. to delete. */
653
char **localArgv; /* Local copy of path. */
654
char *slaveName; /* Last component in path. */
655
char *masterPath; /* One-before-last component in path.*/
656
657
masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord", NULL);
658
if (masterPtr == (Master *) NULL) {
659
panic("DeleteInterpObject: could not find master record");
660
}
661
if (Tcl_SplitList(interp, path, &localArgc, &localArgv) != TCL_OK) {
662
Tcl_AppendResult(interp, "bad interpreter path \"", path,
663
"\"", (char *) NULL);
664
return TCL_ERROR;
665
}
666
if (localArgc < 2) {
667
masterInterp = interp;
668
if (localArgc == 0) {
669
slaveName = "";
670
} else {
671
slaveName = localArgv[0];
672
}
673
} else {
674
masterPath = Tcl_Merge(localArgc-1, localArgv);
675
masterInterp = GetInterp(interp, masterPtr, masterPath, &masterPtr);
676
if (masterInterp == (Tcl_Interp *) NULL) {
677
Tcl_AppendResult(interp, "interpreter named \"", masterPath,
678
"\" not found", (char *) NULL);
679
ckfree((char *) localArgv);
680
ckfree((char *) masterPath);
681
return TCL_ERROR;
682
}
683
ckfree((char *) masterPath);
684
slaveName = localArgv[localArgc-1];
685
}
686
hPtr = Tcl_FindHashEntry(&(masterPtr->slaveTable), slaveName);
687
if (hPtr == (Tcl_HashEntry *) NULL) {
688
ckfree((char *) localArgv);
689
Tcl_AppendResult(interp, "interpreter named \"", path,
690
"\" not found", (char *) NULL);
691
return TCL_ERROR;
692
}
693
slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
694
slaveName = Tcl_GetCommandName(masterInterp, slavePtr->interpCmd);
695
if (Tcl_DeleteCommand(masterInterp, slaveName) != 0) {
696
ckfree((char *) localArgv);
697
Tcl_AppendResult(interp, "interpreter named \"", path,
698
"\" not found", (char *) NULL);
699
return TCL_ERROR;
700
}
701
ckfree((char *) localArgv);
702
return TCL_OK;
703
}
704
705
/*
706
*----------------------------------------------------------------------
707
*
708
* DeleteInterpObject --
709
*
710
* Helper function to do the work of deleting zero or more
711
* interpreters and their interpreter object commands.
712
*
713
* Results:
714
* A standard Tcl result.
715
*
716
* Side effects:
717
* Deletes interpreters and their interpreter object command.
718
*
719
*----------------------------------------------------------------------
720
*/
721
722
static int
723
DeleteInterpObject(interp, argc, argv)
724
Tcl_Interp *interp; /* Interpreter start search from. */
725
int argc; /* Number of arguments in vector. */
726
char **argv; /* Contains path to interps to
727
* delete. */
728
{
729
int i;
730
731
for (i = 2; i < argc; i++) {
732
if (DeleteOneInterpObject(interp, argv[i]) != TCL_OK) {
733
return TCL_ERROR;
734
}
735
}
736
return TCL_OK;
737
}
738
739
/*
740
*----------------------------------------------------------------------
741
*
742
* AliasHelper --
743
*
744
* Helper function to do the work to actually create an alias or
745
* delete an alias.
746
*
747
* Results:
748
* A standard Tcl result.
749
*
750
* Side effects:
751
* An alias command is created and entered into the alias table
752
* for the slave interpreter.
753
*
754
*----------------------------------------------------------------------
755
*/
756
757
static int
758
AliasHelper(curInterp, slaveInterp, masterInterp, masterPtr,
759
aliasName, targetName, argc, argv)
760
Tcl_Interp *curInterp; /* Interp that invoked this proc. */
761
Tcl_Interp *slaveInterp; /* Interp where alias cmd will live
762
* or from which alias will be
763
* deleted. */
764
Tcl_Interp *masterInterp; /* Interp where target cmd will be. */
765
Master *masterPtr; /* Master record for target interp. */
766
char *aliasName; /* Name of alias cmd. */
767
char *targetName; /* Name of target cmd. */
768
int argc; /* Additional arguments to store */
769
char **argv; /* with alias. */
770
{
771
Alias *aliasPtr; /* Storage for alias data. */
772
Alias *tmpAliasPtr; /* Temp storage for alias to delete. */
773
char *tmpAliasName; /* Temp storage for name of alias
774
* to delete. */
775
Tcl_HashEntry *hPtr; /* Entry into interp hashtable. */
776
int i; /* Loop index. */
777
int new; /* Is it a new hash entry? */
778
Target *targetPtr; /* Maps from target command in master
779
* to source command in slave. */
780
Slave *slavePtr; /* Maps from source command in slave
781
* to target command in master. */
782
783
slavePtr = (Slave *) Tcl_GetAssocData(slaveInterp, "tclSlaveRecord", NULL);
784
785
/*
786
* Fix it up if there is no slave record. This can happen if someone
787
* uses "" as the source for an alias.
788
*/
789
790
if (slavePtr == (Slave *) NULL) {
791
slavePtr = (Slave *) ckalloc((unsigned) sizeof(Slave));
792
slavePtr->masterInterp = (Tcl_Interp *) NULL;
793
slavePtr->slaveEntry = (Tcl_HashEntry *) NULL;
794
slavePtr->slaveInterp = slaveInterp;
795
slavePtr->interpCmd = (Tcl_Command) NULL;
796
Tcl_InitHashTable(&(slavePtr->aliasTable), TCL_STRING_KEYS);
797
(void) Tcl_SetAssocData(slaveInterp, "tclSlaveRecord",
798
SlaveRecordDeleteProc, (ClientData) slavePtr);
799
}
800
801
if ((targetName == (char *) NULL) || (targetName[0] == '\0')) {
802
if (argc != 0) {
803
Tcl_AppendResult(curInterp, "malformed command: should be",
804
" \"alias ", aliasName, " {}\"", (char *) NULL);
805
return TCL_ERROR;
806
}
807
808
return DeleteAlias(curInterp, slaveInterp, aliasName);
809
}
810
811
aliasPtr = (Alias *) ckalloc((unsigned) sizeof(Alias));
812
aliasPtr->aliasName = (char *) ckalloc((unsigned) strlen(aliasName)+1);
813
aliasPtr->targetName = (char *) ckalloc((unsigned) strlen(targetName)+1);
814
strcpy(aliasPtr->aliasName, aliasName);
815
strcpy(aliasPtr->targetName, targetName);
816
aliasPtr->targetInterp = masterInterp;
817
818
aliasPtr->argv = (char **) NULL;
819
aliasPtr->argc = argc;
820
if (aliasPtr->argc > 0) {
821
aliasPtr->argv = (char **) ckalloc((unsigned) sizeof(char *) *
822
aliasPtr->argc);
823
for (i = 0; i < argc; i++) {
824
aliasPtr->argv[i] = (char *) ckalloc((unsigned) strlen(argv[i])+1);
825
strcpy(aliasPtr->argv[i], argv[i]);
826
}
827
}
828
829
if (TclPreventAliasLoop(curInterp, slaveInterp, aliasName, AliasCmd,
830
(ClientData) aliasPtr) != TCL_OK) {
831
for (i = 0; i < argc; i++) {
832
ckfree(aliasPtr->argv[i]);
833
}
834
if (aliasPtr->argv != (char **) NULL) {
835
ckfree((char *) aliasPtr->argv);
836
}
837
ckfree(aliasPtr->aliasName);
838
ckfree(aliasPtr->targetName);
839
ckfree((char *) aliasPtr);
840
841
return TCL_ERROR;
842
}
843
844
aliasPtr->slaveCmd = Tcl_CreateCommand(slaveInterp, aliasName, AliasCmd,
845
(ClientData) aliasPtr, AliasCmdDeleteProc);
846
847
/*
848
* Make an entry in the alias table. If it already exists delete
849
* the alias command. Then retry.
850
*/
851
852
do {
853
hPtr = Tcl_CreateHashEntry(&(slavePtr->aliasTable), aliasName, &new);
854
if (new == 0) {
855
tmpAliasPtr = (Alias *) Tcl_GetHashValue(hPtr);
856
tmpAliasName = Tcl_GetCommandName(slaveInterp,
857
tmpAliasPtr->slaveCmd);
858
(void) Tcl_DeleteCommand(slaveInterp, tmpAliasName);
859
860
/*
861
* The hash entry should be deleted by the Tcl_DeleteCommand
862
* above, in its command deletion callback (most likely this
863
* will be AliasCmdDeleteProc, which does the deletion).
864
*/
865
}
866
} while (new == 0);
867
aliasPtr->aliasEntry = hPtr;
868
Tcl_SetHashValue(hPtr, (ClientData) aliasPtr);
869
870
/*
871
* Create the new command. We must do it after deleting any old command,
872
* because the alias may be pointing at a renamed alias, as in:
873
*
874
* interp alias {} foo {} bar # Create an alias "foo"
875
* rename foo zop # Now rename the alias
876
* interp alias {} foo {} zop # Now recreate "foo"...
877
*/
878
879
targetPtr = (Target *) ckalloc((unsigned) sizeof(Target));
880
targetPtr->slaveCmd = aliasPtr->slaveCmd;
881
targetPtr->slaveInterp = slaveInterp;
882
883
do {
884
hPtr = Tcl_CreateHashEntry(&(masterPtr->targetTable),
885
(char *) aliasCounter, &new);
886
aliasCounter++;
887
} while (new == 0);
888
889
Tcl_SetHashValue(hPtr, (ClientData) targetPtr);
890
891
aliasPtr->targetEntry = hPtr;
892
893
curInterp->result = aliasPtr->aliasName;
894
895
return TCL_OK;
896
}
897
898
/*
899
*----------------------------------------------------------------------
900
*
901
* SlaveAliasHelper -
902
*
903
* Handles the different forms of the "interp alias" command:
904
* - interp alias slavePath aliasName
905
* Describes an alias.
906
* - interp alias slavePath aliasName {}
907
* Deletes an alias.
908
* - interp alias slavePath srcCmd masterPath targetCmd args...
909
* Creates an alias.
910
*
911
* Results:
912
* A Tcl result.
913
*
914
* Side effects:
915
* See user documentation for details.
916
*
917
*----------------------------------------------------------------------
918
*/
919
920
static int
921
SlaveAliasHelper(interp, argc, argv)
922
Tcl_Interp *interp; /* Current interpreter. */
923
int argc; /* Number of arguments. */
924
char **argv; /* Argument strings. */
925
{
926
Master *masterPtr; /* Master record for current interp. */
927
Tcl_Interp *slaveInterp, /* Interpreters used when */
928
*masterInterp; /* creating an alias btn siblings. */
929
Master *masterMasterPtr; /* Master record for master interp. */
930
931
masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord", NULL);
932
if (masterPtr == (Master *) NULL) {
933
panic("SlaveAliasHelper: could not find master record");
934
}
935
if (argc < 4) {
936
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
937
" alias slavePath slaveCmd masterPath masterCmd ?args ..?\"",
938
(char *) NULL);
939
return TCL_ERROR;
940
}
941
slaveInterp = GetInterp(interp, masterPtr, argv[2], NULL);
942
if (slaveInterp == (Tcl_Interp *) NULL) {
943
Tcl_AppendResult(interp, "could not find interpreter \"",
944
argv[2], "\"", (char *) NULL);
945
return TCL_ERROR;
946
}
947
if (argc == 4) {
948
return DescribeAlias(interp, slaveInterp, argv[3]);
949
}
950
if (argc == 5 && strcmp(argv[4], "") == 0) {
951
return DeleteAlias(interp, slaveInterp, argv[3]);
952
}
953
if (argc < 6) {
954
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
955
" alias slavePath slaveCmd masterPath masterCmd ?args ..?\"",
956
(char *) NULL);
957
return TCL_ERROR;
958
}
959
masterInterp = GetInterp(interp, masterPtr, argv[4], &masterMasterPtr);
960
if (masterInterp == (Tcl_Interp *) NULL) {
961
Tcl_AppendResult(interp, "could not find interpreter \"",
962
argv[4], "\"", (char *) NULL);
963
return TCL_ERROR;
964
}
965
return AliasHelper(interp, slaveInterp, masterInterp, masterMasterPtr,
966
argv[3], argv[5], argc-6, argv+6);
967
}
968
969
/*
970
*----------------------------------------------------------------------
971
*
972
* DescribeAlias --
973
*
974
* Sets interp->result to a Tcl list describing the given alias in the
975
* given interpreter: its target command and the additional arguments
976
* to prepend to any invocation of the alias.
977
*
978
* Results:
979
* A standard Tcl result.
980
*
981
* Side effects:
982
* None.
983
*
984
*----------------------------------------------------------------------
985
*/
986
987
static int
988
DescribeAlias(interp, slaveInterp, aliasName)
989
Tcl_Interp *interp; /* Interpreter for result and errors. */
990
Tcl_Interp *slaveInterp; /* Interpreter defining alias. */
991
char *aliasName; /* Name of alias to describe. */
992
{
993
Slave *slavePtr; /* Slave record for slave interpreter. */
994
Tcl_HashEntry *hPtr; /* Search variable. */
995
Alias *aliasPtr; /* Structure describing alias. */
996
int i; /* Loop variable. */
997
998
slavePtr = (Slave *) Tcl_GetAssocData(slaveInterp, "tclSlaveRecord",
999
NULL);
1000
if (slavePtr == (Slave *) NULL) {
1001
1002
/*
1003
* It's possible that the interpreter still does not have a slave
1004
* record. If so, create such a record now. This is only possible
1005
* for interpreters that were created with Tcl_CreateInterp, not
1006
* those created with Tcl_CreateSlave, so this interpreter does
1007
* not have a master.
1008
*/
1009
1010
slavePtr = (Slave *) ckalloc((unsigned) sizeof(Slave));
1011
slavePtr->masterInterp = (Tcl_Interp *) NULL;
1012
slavePtr->slaveEntry = (Tcl_HashEntry *) NULL;
1013
slavePtr->slaveInterp = slaveInterp;
1014
slavePtr->interpCmd = (Tcl_Command) NULL;
1015
Tcl_InitHashTable(&(slavePtr->aliasTable), TCL_STRING_KEYS);
1016
(void) Tcl_SetAssocData(slaveInterp, "tclSlaveRecord",
1017
SlaveRecordDeleteProc, (ClientData) slavePtr);
1018
}
1019
hPtr = Tcl_FindHashEntry(&(slavePtr->aliasTable), aliasName);
1020
if (hPtr == (Tcl_HashEntry *) NULL) {
1021
return TCL_OK;
1022
}
1023
aliasPtr = (Alias *) Tcl_GetHashValue(hPtr);
1024
Tcl_AppendResult(interp, aliasPtr->targetName, (char *) NULL);
1025
for (i = 0; i < aliasPtr->argc; i++) {
1026
Tcl_AppendElement(interp, aliasPtr->argv[i]);
1027
}
1028
1029
return TCL_OK;
1030
}
1031
1032
/*
1033
*----------------------------------------------------------------------
1034
*
1035
* DeleteAlias --
1036
*
1037
* Deletes the given alias from the slave interpreter given.
1038
*
1039
* Results:
1040
* A standard Tcl result.
1041
*
1042
* Side effects:
1043
* Deletes the alias from the slave interpreter.
1044
*
1045
*----------------------------------------------------------------------
1046
*/
1047
1048
static int
1049
DeleteAlias(interp, slaveInterp, aliasName)
1050
Tcl_Interp *interp; /* Interpreter for result and errors. */
1051
Tcl_Interp *slaveInterp; /* Interpreter defining alias. */
1052
char *aliasName; /* Name of alias to delete. */
1053
{
1054
Slave *slavePtr; /* Slave record for slave interpreter. */
1055
Tcl_HashEntry *hPtr; /* Search variable. */
1056
Alias *aliasPtr; /* Structure describing alias to delete. */
1057
1058
slavePtr = (Slave *) Tcl_GetAssocData(slaveInterp, "tclSlaveRecord",
1059
NULL);
1060
if (slavePtr == (Slave *) NULL) {
1061
Tcl_AppendResult(interp, "alias \"", aliasName, "\" not found",
1062
(char *) NULL);
1063
return TCL_ERROR;
1064
}
1065
1066
/*
1067
* Get the alias from the alias table, determine the current
1068
* true name of the alias (it may have been renamed!) and then
1069
* delete the true command name. The deleteProc on the alias
1070
* command will take care of removing the entry from the alias
1071
* table.
1072
*/
1073
1074
hPtr = Tcl_FindHashEntry(&(slavePtr->aliasTable), aliasName);
1075
if (hPtr == (Tcl_HashEntry *) NULL) {
1076
Tcl_AppendResult(interp, "alias \"", aliasName, "\" not found",
1077
(char *) NULL);
1078
return TCL_ERROR;
1079
}
1080
aliasPtr = (Alias *) Tcl_GetHashValue(hPtr);
1081
aliasName = Tcl_GetCommandName(slaveInterp, aliasPtr->slaveCmd);
1082
1083
/*
1084
* NOTE: The deleteProc for this command will delete the
1085
* alias from the hash table. The deleteProc will also
1086
* delete the target information from the master interpreter
1087
* target table.
1088
*/
1089
1090
if (Tcl_DeleteCommand(slaveInterp, aliasName) != 0) {
1091
panic("DeleteAlias: did not find alias to be deleted");
1092
}
1093
1094
return TCL_OK;
1095
}
1096
1097
/*
1098
*----------------------------------------------------------------------
1099
*
1100
* Tcl_GetInterpPath --
1101
*
1102
* Sets the result of the asking interpreter to a proper Tcl list
1103
* containing the names of interpreters between the asking and
1104
* target interpreters. The target interpreter must be either the
1105
* same as the asking interpreter or one of its slaves (including
1106
* recursively).
1107
*
1108
* Results:
1109
* TCL_OK if the target interpreter is the same as, or a descendant
1110
* of, the asking interpreter; TCL_ERROR else. This way one can
1111
* distinguish between the case where the asking and target interps
1112
* are the same (an empty list is the result, and TCL_OK is returned)
1113
* and when the target is not a descendant of the asking interpreter
1114
* (in which case the Tcl result is an error message and the function
1115
* returns TCL_ERROR).
1116
*
1117
* Side effects:
1118
* None.
1119
*
1120
*----------------------------------------------------------------------
1121
*/
1122
1123
int
1124
Tcl_GetInterpPath(askingInterp, targetInterp)
1125
Tcl_Interp *askingInterp; /* Interpreter to start search from. */
1126
Tcl_Interp *targetInterp; /* Interpreter to find. */
1127
{
1128
Master *masterPtr; /* Interim storage for Master record. */
1129
Slave *slavePtr; /* Interim storage for Slave record. */
1130
1131
if (targetInterp == askingInterp) {
1132
return TCL_OK;
1133
}
1134
if (targetInterp == (Tcl_Interp *) NULL) {
1135
return TCL_ERROR;
1136
}
1137
slavePtr = (Slave *) Tcl_GetAssocData(targetInterp, "tclSlaveRecord",
1138
NULL);
1139
if (slavePtr == (Slave *) NULL) {
1140
return TCL_ERROR;
1141
}
1142
if (Tcl_GetInterpPath(askingInterp, slavePtr->masterInterp) == TCL_ERROR) {
1143
/*
1144
* AskingInterp->result was set by recursive call.
1145
*/
1146
return TCL_ERROR;
1147
}
1148
masterPtr = (Master *) Tcl_GetAssocData(slavePtr->masterInterp,
1149
"tclMasterRecord", NULL);
1150
if (masterPtr == (Master *) NULL) {
1151
panic("Tcl_GetInterpPath: could not find master record");
1152
}
1153
Tcl_AppendElement(askingInterp, Tcl_GetHashKey(&(masterPtr->slaveTable),
1154
slavePtr->slaveEntry));
1155
return TCL_OK;
1156
}
1157
1158
/*
1159
*----------------------------------------------------------------------
1160
*
1161
* GetTarget --
1162
*
1163
* Sets the result of the invoking interpreter to a path name for
1164
* the target interpreter of an alias in one of the slaves.
1165
*
1166
* Results:
1167
* TCL_OK if the target interpreter of the alias is a slave of the
1168
* invoking interpreter, TCL_ERROR else.
1169
*
1170
* Side effects:
1171
* Sets the result of the invoking interpreter.
1172
*
1173
*----------------------------------------------------------------------
1174
*/
1175
1176
static int
1177
GetTarget(askingInterp, path, aliasName)
1178
Tcl_Interp *askingInterp; /* Interpreter to start search from. */
1179
char *path; /* The path of the interp to find. */
1180
char *aliasName; /* The target of this allias. */
1181
{
1182
Tcl_Interp *slaveInterp; /* Interim storage for slave. */
1183
Slave *slaveSlavePtr; /* Its Slave record. */
1184
Master *masterPtr; /* Interim storage for Master record. */
1185
Tcl_HashEntry *hPtr; /* Search element. */
1186
Alias *aliasPtr; /* Data describing the alias. */
1187
1188
Tcl_ResetResult(askingInterp);
1189
1190
masterPtr = (Master *) Tcl_GetAssocData(askingInterp, "tclMasterRecord",
1191
NULL);
1192
if (masterPtr == (Master *) NULL) {
1193
panic("GetTarget: could not find master record");
1194
}
1195
slaveInterp = GetInterp(askingInterp, masterPtr, path, NULL);
1196
if (slaveInterp == (Tcl_Interp *) NULL) {
1197
Tcl_AppendResult(askingInterp, "could not find interpreter \"",
1198
path, "\"", (char *) NULL);
1199
return TCL_ERROR;
1200
}
1201
slaveSlavePtr = (Slave *) Tcl_GetAssocData(slaveInterp, "tclSlaveRecord",
1202
NULL);
1203
if (slaveSlavePtr == (Slave *) NULL) {
1204
panic("GetTarget: could not find slave record");
1205
}
1206
hPtr = Tcl_FindHashEntry(&(slaveSlavePtr->aliasTable), aliasName);
1207
if (hPtr == (Tcl_HashEntry *) NULL) {
1208
Tcl_AppendResult(askingInterp, "alias \"", aliasName, "\" in path \"",
1209
path, "\" not found", (char *) NULL);
1210
return TCL_ERROR;
1211
}
1212
aliasPtr = (Alias *) Tcl_GetHashValue(hPtr);
1213
if (aliasPtr == (Alias *) NULL) {
1214
panic("GetTarget: could not find alias record");
1215
}
1216
if (Tcl_GetInterpPath(askingInterp, aliasPtr->targetInterp) == TCL_ERROR) {
1217
Tcl_ResetResult(askingInterp);
1218
Tcl_AppendResult(askingInterp, "target interpreter for alias \"",
1219
aliasName, "\" in path \"", path, "\" is not my descendant",
1220
(char *) NULL);
1221
return TCL_ERROR;
1222
}
1223
return TCL_OK;
1224
}
1225
1226
/*
1227
*----------------------------------------------------------------------
1228
*
1229
* Tcl_InterpCmd --
1230
*
1231
* This procedure is invoked to process the "interp" Tcl command.
1232
* See the user documentation for details on what it does.
1233
*
1234
* Results:
1235
* A standard Tcl result.
1236
*
1237
* Side effects:
1238
* See the user documentation.
1239
*
1240
*----------------------------------------------------------------------
1241
*/
1242
/* ARGSUSED */
1243
int
1244
Tcl_InterpCmd(clientData, interp, argc, argv)
1245
ClientData clientData; /* Unused. */
1246
Tcl_Interp *interp; /* Current interpreter. */
1247
int argc; /* Number of arguments. */
1248
char **argv; /* Argument strings. */
1249
{
1250
Tcl_Interp *slaveInterp; /* A slave. */
1251
Tcl_Interp *masterInterp; /* A master. */
1252
Master *masterPtr; /* Master record for current interp. */
1253
Slave *slavePtr; /* Record for slave interp. */
1254
Tcl_HashEntry *hPtr; /* Search variable. */
1255
Tcl_HashSearch hSearch; /* Iteration variable. */
1256
size_t len; /* Length of command name. */
1257
int result; /* Result of eval. */
1258
char *cmdName; /* Name of sub command to do. */
1259
char *cmd; /* Command to eval. */
1260
Tcl_Channel chan; /* Channel to share or transfer. */
1261
1262
if (argc < 2) {
1263
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1264
" cmd ?arg ...?\"", (char *) NULL);
1265
return TCL_ERROR;
1266
}
1267
cmdName = argv[1];
1268
1269
masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord", NULL);
1270
if (masterPtr == (Master *) NULL) {
1271
panic("Tcl_InterpCmd: could not find master record");
1272
}
1273
1274
len = strlen(cmdName);
1275
1276
if (cmdName[0] == 'a') {
1277
if ((strncmp(cmdName, "alias", len) == 0) && (len <= 5)) {
1278
return SlaveAliasHelper(interp, argc, argv);
1279
}
1280
1281
if (strcmp(cmdName, "aliases") == 0) {
1282
if (argc != 2 && argc != 3) {
1283
Tcl_AppendResult(interp, "wrong # args: should be \"",
1284
argv[0], " aliases ?path?\"", (char *) NULL);
1285
return TCL_ERROR;
1286
}
1287
if (argc == 3) {
1288
slaveInterp = GetInterp(interp, masterPtr, argv[2], NULL);
1289
if (slaveInterp == (Tcl_Interp *) NULL) {
1290
Tcl_AppendResult(interp, "interpreter \"",
1291
argv[2], "\" not found", (char *) NULL);
1292
return TCL_ERROR;
1293
}
1294
} else {
1295
slaveInterp = interp;
1296
}
1297
slavePtr = (Slave *) Tcl_GetAssocData(slaveInterp,
1298
"tclSlaveRecord", NULL);
1299
if (slavePtr == (Slave *) NULL) {
1300
return TCL_OK;
1301
}
1302
for (hPtr = Tcl_FirstHashEntry(&(slavePtr->aliasTable), &hSearch);
1303
hPtr != NULL;
1304
hPtr = Tcl_NextHashEntry(&hSearch)) {
1305
Tcl_AppendElement(interp,
1306
Tcl_GetHashKey(&(slavePtr->aliasTable), hPtr));
1307
}
1308
return TCL_OK;
1309
}
1310
}
1311
1312
if ((cmdName[0] == 'c') && (strncmp(cmdName, "create", len) == 0)) {
1313
return CreateInterpObject(interp, argc, argv);
1314
}
1315
1316
if ((cmdName[0] == 'd') && (strncmp(cmdName, "delete", len) == 0)) {
1317
return DeleteInterpObject(interp, argc, argv);
1318
}
1319
1320
if (cmdName[0] == 'e') {
1321
if ((strncmp(cmdName, "eval", len) == 0) && (len > 1)) {
1322
if (argc < 4) {
1323
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1324
" eval path arg ?arg ...?\"", (char *) NULL);
1325
return TCL_ERROR;
1326
}
1327
slaveInterp = GetInterp(interp, masterPtr, argv[2], NULL);
1328
if (slaveInterp == (Tcl_Interp *) NULL) {
1329
Tcl_AppendResult(interp, "interpreter named \"", argv[2],
1330
"\" not found", (char *) NULL);
1331
return TCL_ERROR;
1332
}
1333
cmd = Tcl_Concat(argc-3, argv+3);
1334
Tcl_Preserve((ClientData) slaveInterp);
1335
result = Tcl_Eval(slaveInterp, cmd);
1336
ckfree((char *) cmd);
1337
1338
/*
1339
* Now make the result and any error information accessible. We
1340
* have to be careful because the slave interpreter and the current
1341
* interpreter can be the same - do not destroy the result.. This
1342
* can happen if an interpreter contains an alias which is directed
1343
* at a target command in the same interpreter.
1344
*/
1345
1346
if (interp != slaveInterp) {
1347
if (result == TCL_ERROR) {
1348
1349
/*
1350
* An error occurred, so transfer error information from
1351
* the target interpreter back to our interpreter. Must
1352
* clear interp's result before calling Tcl_AddErrorInfo,
1353
* since Tcl_AddErrorInfo will store the interp's result in
1354
* errorInfo before appending slaveInterp's $errorInfo;
1355
* we've already got everything we need in the slave
1356
* interpreter's $errorInfo.
1357
*/
1358
1359
Tcl_ResetResult(interp);
1360
Tcl_AddErrorInfo(interp, Tcl_GetVar2(slaveInterp,
1361
"errorInfo", (char *) NULL, TCL_GLOBAL_ONLY));
1362
Tcl_SetVar2(interp, "errorCode", (char *) NULL,
1363
Tcl_GetVar2(slaveInterp, "errorCode", (char *)
1364
NULL, TCL_GLOBAL_ONLY),
1365
TCL_GLOBAL_ONLY);
1366
}
1367
if (slaveInterp->freeProc != NULL) {
1368
interp->result = slaveInterp->result;
1369
interp->freeProc = slaveInterp->freeProc;
1370
slaveInterp->freeProc = 0;
1371
} else {
1372
Tcl_SetResult(interp, slaveInterp->result, TCL_VOLATILE);
1373
}
1374
Tcl_ResetResult(slaveInterp);
1375
}
1376
Tcl_Release((ClientData) slaveInterp);
1377
return result;
1378
}
1379
if ((strncmp(cmdName, "exists", len) == 0) && (len > 2)) {
1380
if (argc > 3) {
1381
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1382
" exists ?path?\"", (char *) NULL);
1383
return TCL_ERROR;
1384
}
1385
if (argc == 3) {
1386
if (GetInterp(interp, masterPtr, argv[2], NULL) ==
1387
(Tcl_Interp *) NULL) {
1388
Tcl_AppendResult(interp, "0", (char *) NULL);
1389
} else {
1390
Tcl_AppendResult(interp, "1", (char *) NULL);
1391
}
1392
} else {
1393
Tcl_AppendResult(interp, "1", (char *) NULL);
1394
}
1395
return TCL_OK;
1396
}
1397
}
1398
1399
if (cmdName[0] == 'i') {
1400
if ((len > 1) && (strncmp(cmdName, "issafe", len) == 0)) {
1401
if (argc > 3) {
1402
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1403
" issafe ?path?\"", (char *) NULL);
1404
return TCL_ERROR;
1405
}
1406
if (argc == 3) {
1407
slaveInterp = GetInterp(interp, masterPtr, argv[2],
1408
&masterPtr);
1409
if (slaveInterp == (Tcl_Interp *) NULL) {
1410
Tcl_AppendResult(interp, "interpreter \"", argv[2],
1411
"\" not found", (char *) NULL);
1412
return TCL_ERROR;
1413
}
1414
}
1415
if (masterPtr->isSafe == 0) {
1416
Tcl_AppendResult(interp, "0", (char *) NULL);
1417
} else {
1418
Tcl_AppendResult(interp, "1", (char *) NULL);
1419
}
1420
return TCL_OK;
1421
}
1422
}
1423
1424
if (cmdName[0] == 's') {
1425
if ((strncmp(cmdName, "slaves", len) == 0) && (len > 1)) {
1426
if (argc != 2 && argc != 3) {
1427
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1428
" slaves ?path?\"", (char *) NULL);
1429
return TCL_ERROR;
1430
}
1431
if (argc == 3) {
1432
if (GetInterp(interp, masterPtr, argv[2], &masterPtr) ==
1433
(Tcl_Interp *) NULL) {
1434
Tcl_AppendResult(interp, "interpreter \"", argv[2],
1435
"\" not found", (char *) NULL);
1436
return TCL_ERROR;
1437
}
1438
}
1439
for (hPtr = Tcl_FirstHashEntry(&(masterPtr->slaveTable), &hSearch);
1440
hPtr != NULL;
1441
hPtr = Tcl_NextHashEntry(&hSearch)) {
1442
Tcl_AppendElement(interp,
1443
Tcl_GetHashKey(&(masterPtr->slaveTable), hPtr));
1444
}
1445
return TCL_OK;
1446
}
1447
if ((strncmp(cmdName, "share", len) == 0) && (len > 1)) {
1448
if (argc != 5) {
1449
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1450
" share srcPath channelId destPath\"", (char *) NULL);
1451
return TCL_ERROR;
1452
}
1453
masterInterp = GetInterp(interp, masterPtr, argv[2], NULL);
1454
if (masterInterp == (Tcl_Interp *) NULL) {
1455
Tcl_AppendResult(interp, "interpreter \"", argv[2],
1456
"\" not found", (char *) NULL);
1457
return TCL_ERROR;
1458
}
1459
slaveInterp = GetInterp(interp, masterPtr, argv[4], NULL);
1460
if (slaveInterp == (Tcl_Interp *) NULL) {
1461
Tcl_AppendResult(interp, "interpreter \"", argv[4],
1462
"\" not found", (char *) NULL);
1463
return TCL_ERROR;
1464
}
1465
chan = Tcl_GetChannel(masterInterp, argv[3], NULL);
1466
if (chan == (Tcl_Channel) NULL) {
1467
if (interp != masterInterp) {
1468
Tcl_AppendResult(interp, masterInterp->result,
1469
(char *) NULL);
1470
Tcl_ResetResult(masterInterp);
1471
}
1472
return TCL_ERROR;
1473
}
1474
Tcl_RegisterChannel(slaveInterp, chan);
1475
return TCL_OK;
1476
}
1477
}
1478
1479
if ((cmdName[0] == 't') && (strncmp(cmdName, "target", len) == 0)) {
1480
if (argc != 4) {
1481
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1482
" target path alias\"", (char *) NULL);
1483
return TCL_ERROR;
1484
}
1485
return GetTarget(interp, argv[2], argv[3]);
1486
}
1487
1488
if ((cmdName[0] == 't') && (strncmp(cmdName, "transfer", len) == 0)) {
1489
if (argc != 5) {
1490
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1491
" transfer srcPath channelId destPath\"", (char *) NULL);
1492
return TCL_ERROR;
1493
}
1494
masterInterp = GetInterp(interp, masterPtr, argv[2], NULL);
1495
if (masterInterp == (Tcl_Interp *) NULL) {
1496
Tcl_AppendResult(interp, "interpreter \"", argv[2],
1497
"\" not found", (char *) NULL);
1498
return TCL_ERROR;
1499
}
1500
slaveInterp = GetInterp(interp, masterPtr, argv[4], NULL);
1501
if (slaveInterp == (Tcl_Interp *) NULL) {
1502
Tcl_AppendResult(interp, "interpreter \"", argv[4],
1503
"\" not found", (char *) NULL);
1504
return TCL_ERROR;
1505
}
1506
chan = Tcl_GetChannel(masterInterp, argv[3], NULL);
1507
if (chan == (Tcl_Channel) NULL) {
1508
if (interp != masterInterp) {
1509
Tcl_AppendResult(interp, masterInterp->result, (char *) NULL);
1510
Tcl_ResetResult(masterInterp);
1511
}
1512
return TCL_ERROR;
1513
}
1514
Tcl_RegisterChannel(slaveInterp, chan);
1515
if (Tcl_UnregisterChannel(masterInterp, chan) != TCL_OK) {
1516
if (interp != masterInterp) {
1517
Tcl_AppendResult(interp, masterInterp->result, (char *) NULL);
1518
Tcl_ResetResult(masterInterp);
1519
}
1520
return TCL_ERROR;
1521
}
1522
1523
return TCL_OK;
1524
}
1525
1526
Tcl_AppendResult(interp, "bad option \"", argv[1],
1527
"\": should be alias, aliases, create, delete, exists, eval, ",
1528
"issafe, share, slaves, target or transfer", (char *) NULL);
1529
return TCL_ERROR;
1530
}
1531
1532
/*
1533
*----------------------------------------------------------------------
1534
*
1535
* SlaveObjectCmd --
1536
*
1537
* Command to manipulate an interpreter, e.g. to send commands to it
1538
* to be evaluated. One such command exists for each slave interpreter.
1539
*
1540
* Results:
1541
* A standard Tcl result.
1542
*
1543
* Side effects:
1544
* See user documentation for details.
1545
*
1546
*----------------------------------------------------------------------
1547
*/
1548
1549
static int
1550
SlaveObjectCmd(clientData, interp, argc, argv)
1551
ClientData clientData; /* Slave interpreter. */
1552
Tcl_Interp *interp; /* Current interpreter. */
1553
int argc; /* Number of arguments. */
1554
char **argv; /* Argument strings. */
1555
{
1556
Master *masterPtr; /* Master record for slave interp. */
1557
Slave *slavePtr; /* Slave record. */
1558
Tcl_Interp *slaveInterp; /* Slave interpreter. */
1559
char *cmdName; /* Name of command to do. */
1560
char *cmd; /* Command to evaluate in slave
1561
* interpreter. */
1562
Alias *aliasPtr; /* Alias information. */
1563
Tcl_HashEntry *hPtr; /* For local searches. */
1564
Tcl_HashSearch hSearch; /* For local searches. */
1565
int result; /* Loop counter, status return. */
1566
size_t len; /* Length of command name. */
1567
1568
if (argc < 2) {
1569
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1570
" cmd ?arg ...?\"", (char *) NULL);
1571
return TCL_ERROR;
1572
}
1573
1574
slaveInterp = (Tcl_Interp *) clientData;
1575
if (slaveInterp == (Tcl_Interp *) NULL) {
1576
Tcl_AppendResult(interp, "interpreter ", argv[0], " has been deleted",
1577
(char *) NULL);
1578
return TCL_ERROR;
1579
}
1580
1581
slavePtr = (Slave *) Tcl_GetAssocData(slaveInterp,
1582
"tclSlaveRecord", NULL);
1583
if (slavePtr == (Slave *) NULL) {
1584
panic("SlaveObjectCmd: could not find slave record");
1585
}
1586
1587
cmdName = argv[1];
1588
len = strlen(cmdName);
1589
1590
if (cmdName[0] == 'a') {
1591
if (strncmp(cmdName, "alias", len) == 0) {
1592
switch (argc-2) {
1593
case 0:
1594
Tcl_AppendResult(interp, "wrong # args: should be \"",
1595
argv[0], " alias aliasName ?targetName? ?args..?",
1596
(char *) NULL);
1597
return TCL_ERROR;
1598
1599
case 1:
1600
1601
/*
1602
* Return the name of the command in the current
1603
* interpreter for which the argument is an alias in the
1604
* slave interpreter, and the list of saved arguments
1605
*/
1606
1607
return DescribeAlias(interp, slaveInterp, argv[2]);
1608
1609
default:
1610
masterPtr = (Master *) Tcl_GetAssocData(interp,
1611
"tclMasterRecord", NULL);
1612
if (masterPtr == (Master *) NULL) {
1613
panic("SlaveObjectCmd: could not find master record");
1614
}
1615
return AliasHelper(interp, slaveInterp, interp, masterPtr,
1616
argv[2], argv[3], argc-4, argv+4);
1617
}
1618
}
1619
1620
if (strncmp(cmdName, "aliases", len) == 0) {
1621
1622
/*
1623
* Return the names of all the aliases created in the
1624
* slave interpreter.
1625
*/
1626
1627
for (hPtr = Tcl_FirstHashEntry(&(slavePtr->aliasTable),
1628
&hSearch);
1629
hPtr != (Tcl_HashEntry *) NULL;
1630
hPtr = Tcl_NextHashEntry(&hSearch)) {
1631
aliasPtr = (Alias *) Tcl_GetHashValue(hPtr);
1632
Tcl_AppendElement(interp, aliasPtr->aliasName);
1633
}
1634
return TCL_OK;
1635
}
1636
}
1637
1638
if (cmdName[0] == 'e') {
1639
if ((len > 1) && (strncmp(cmdName, "eval", len) == 0)) {
1640
if (argc < 3) {
1641
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1642
" eval arg ?arg ...?\"", (char *) NULL);
1643
return TCL_ERROR;
1644
}
1645
1646
cmd = Tcl_Concat(argc-2, argv+2);
1647
Tcl_Preserve((ClientData) slaveInterp);
1648
result = Tcl_Eval(slaveInterp, cmd);
1649
ckfree((char *) cmd);
1650
1651
/*
1652
* Make the result and any error information accessible. We have
1653
* to be careful because the slave interpreter and the current
1654
* interpreter can be the same - do not destroy the result.. This
1655
* can happen if an interpreter contains an alias which is directed
1656
* at a target command in the same interpreter.
1657
*/
1658
1659
if (interp != slaveInterp) {
1660
if (result == TCL_ERROR) {
1661
1662
/*
1663
* An error occurred, so transfer error information from the
1664
* destination interpreter back to our interpreter. Clear
1665
* interp's result before calling Tcl_AddErrorInfo, since
1666
* Tcl_AddErrorInfo stores the interp's result in errorInfo
1667
* before appending slaveInterp's $errorInfo;
1668
* we've already got everything we need in the slave
1669
* interpreter's $errorInfo.
1670
*/
1671
1672
Tcl_ResetResult(interp);
1673
Tcl_AddErrorInfo(interp, Tcl_GetVar2(slaveInterp,
1674
"errorInfo", (char *) NULL, TCL_GLOBAL_ONLY));
1675
Tcl_SetVar2(interp, "errorCode", (char *) NULL,
1676
Tcl_GetVar2(slaveInterp, "errorCode",
1677
(char *) NULL, TCL_GLOBAL_ONLY),
1678
TCL_GLOBAL_ONLY);
1679
}
1680
if (slaveInterp->freeProc != NULL) {
1681
interp->result = slaveInterp->result;
1682
interp->freeProc = slaveInterp->freeProc;
1683
slaveInterp->freeProc = 0;
1684
} else {
1685
Tcl_SetResult(interp, slaveInterp->result, TCL_VOLATILE);
1686
}
1687
Tcl_ResetResult(slaveInterp);
1688
}
1689
Tcl_Release((ClientData) slaveInterp);
1690
return result;
1691
}
1692
}
1693
1694
if (cmdName[0] == 'i') {
1695
if ((len > 1) && (strncmp(cmdName, "issafe", len) == 0)) {
1696
if (argc > 2) {
1697
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1698
" issafe\"", (char *) NULL);
1699
return TCL_ERROR;
1700
}
1701
masterPtr = (Master *) Tcl_GetAssocData(slaveInterp,
1702
"tclMasterRecord", NULL);
1703
if (masterPtr == (Master *) NULL) {
1704
panic("SlaveObjectCmd: could not find master record");
1705
}
1706
if (masterPtr->isSafe == 1) {
1707
Tcl_AppendResult(interp, "1", (char *) NULL);
1708
} else {
1709
Tcl_AppendResult(interp, "0", (char *) NULL);
1710
}
1711
return TCL_OK;
1712
}
1713
}
1714
1715
Tcl_AppendResult(interp, "bad option \"", argv[1],
1716
"\": should be alias, aliases, eval, or issafe", (char *) NULL);
1717
return TCL_ERROR;
1718
}
1719
1720
/*
1721
*----------------------------------------------------------------------
1722
*
1723
* SlaveObjectDeleteProc --
1724
*
1725
* Invoked when an object command for a slave interpreter is deleted;
1726
* cleans up all state associated with the slave interpreter and destroys
1727
* the slave interpreter.
1728
*
1729
* Results:
1730
* None.
1731
*
1732
* Side effects:
1733
* Cleans up all state associated with the slave interpreter and
1734
* destroys the slave interpreter.
1735
*
1736
*----------------------------------------------------------------------
1737
*/
1738
1739
static void
1740
SlaveObjectDeleteProc(clientData)
1741
ClientData clientData; /* The SlaveRecord for the command. */
1742
{
1743
Slave *slavePtr; /* Interim storage for Slave record. */
1744
Tcl_Interp *slaveInterp; /* And for a slave interp. */
1745
1746
slaveInterp = (Tcl_Interp *) clientData;
1747
slavePtr = (Slave *) Tcl_GetAssocData(slaveInterp, "tclSlaveRecord",NULL);
1748
if (slavePtr == (Slave *) NULL) {
1749
panic("SlaveObjectDeleteProc: could not find slave record");
1750
}
1751
1752
/*
1753
* Delete the entry in the slave table in the master interpreter now.
1754
* This is to avoid an infinite loop in the Master hash table cleanup in
1755
* the master interpreter. This can happen if this slave is being deleted
1756
* because the master is being deleted and the slave deletion is deferred
1757
* because it is still active.
1758
*/
1759
1760
Tcl_DeleteHashEntry(slavePtr->slaveEntry);
1761
1762
/*
1763
* Set to NULL so that when the slave record is cleaned up in the slave
1764
* it does not try to delete the command causing all sorts of grief.
1765
* See SlaveRecordDeleteProc().
1766
*/
1767
1768
slavePtr->interpCmd = NULL;
1769
1770
/*
1771
* Destroy the interpreter - this will cause all the deleteProcs for
1772
* all commands (including aliases) to run.
1773
*
1774
* NOTE: WE ASSUME THAT THE INTERPRETER HAS NOT BEEN DELETED YET!!
1775
*/
1776
1777
Tcl_DeleteInterp(slavePtr->slaveInterp);
1778
}
1779
1780
/*
1781
*----------------------------------------------------------------------
1782
*
1783
* AliasCmd --
1784
*
1785
* This is the procedure that services invocations of aliases in a
1786
* slave interpreter. One such command exists for each alias. When
1787
* invoked, this procedure redirects the invocation to the target
1788
* command in the master interpreter as designated by the Alias
1789
* record associated with this command.
1790
*
1791
* Results:
1792
* A standard Tcl result.
1793
*
1794
* Side effects:
1795
* Causes forwarding of the invocation; all possible side effects
1796
* may occur as a result of invoking the command to which the
1797
* invocation is forwarded.
1798
*
1799
*----------------------------------------------------------------------
1800
*/
1801
1802
static int
1803
AliasCmd(clientData, interp, argc, argv)
1804
ClientData clientData; /* Alias record. */
1805
Tcl_Interp *interp; /* Current interpreter. */
1806
int argc; /* Number of arguments. */
1807
char **argv; /* Argument strings. */
1808
{
1809
Alias *aliasPtr; /* Describes the alias. */
1810
Tcl_CmdInfo cmdInfo; /* Info about target command. */
1811
int result; /* Result of execution. */
1812
int i, j, addArgc; /* Loop counters. */
1813
int localArgc; /* Local argument count. */
1814
char **localArgv; /* Local argument vector. */
1815
Interp *iPtr; /* The target interpreter. */
1816
1817
aliasPtr = (Alias *) clientData;
1818
1819
result = Tcl_GetCommandInfo(aliasPtr->targetInterp, aliasPtr->targetName,
1820
&cmdInfo);
1821
if (result == 0) {
1822
Tcl_AppendResult(interp, "aliased target \"", aliasPtr->targetName,
1823
"\" for \"", argv[0], "\" not found", (char *) NULL);
1824
return TCL_ERROR;
1825
}
1826
if (aliasPtr->argc <= 0) {
1827
localArgv = argv;
1828
localArgc = argc;
1829
} else {
1830
addArgc = aliasPtr->argc;
1831
localArgc = argc + addArgc;
1832
localArgv = (char **) ckalloc((unsigned) sizeof(char *) * localArgc);
1833
localArgv[0] = argv[0];
1834
for (i = 0, j = 1; i < addArgc; i++, j++) {
1835
localArgv[j] = aliasPtr->argv[i];
1836
}
1837
for (i = 1; i < argc; i++, j++) {
1838
localArgv[j] = argv[i];
1839
}
1840
}
1841
1842
/*
1843
* Invoke the redirected command in the target interpreter. Note
1844
* that we are not calling eval because of possible security holes with
1845
* $ substitution and bracketed command evaluation.
1846
*
1847
* We duplicate some code here from Tcl_Eval to implement recursion
1848
* level counting and correct deletion of the target interpreter if
1849
* that was requested but delayed because of in-progress evaluations.
1850
*/
1851
1852
iPtr = (Interp *) aliasPtr->targetInterp;
1853
iPtr->numLevels++;
1854
Tcl_Preserve((ClientData) iPtr);
1855
Tcl_ResetResult((Tcl_Interp *) iPtr);
1856
result = (cmdInfo.proc)(cmdInfo.clientData, (Tcl_Interp *) iPtr,
1857
localArgc, localArgv);
1858
iPtr->numLevels--;
1859
if (iPtr->numLevels == 0) {
1860
if (result == TCL_RETURN) {
1861
result = TclUpdateReturnInfo(iPtr);
1862
}
1863
if ((result != TCL_OK) && (result != TCL_ERROR)) {
1864
Tcl_ResetResult((Tcl_Interp *) iPtr);
1865
if (result == TCL_BREAK) {
1866
iPtr->result = "invoked \"break\" outside of a loop";
1867
} else if (result == TCL_CONTINUE) {
1868
iPtr->result = "invoked \"continue\" outside of a loop";
1869
} else {
1870
iPtr->result = iPtr->resultSpace;
1871
sprintf(iPtr->resultSpace, "command returned bad code: %d",
1872
result);
1873
}
1874
result = TCL_ERROR;
1875
}
1876
}
1877
1878
/*
1879
* Clean up any locally allocated argument vector structure.
1880
*/
1881
1882
if (localArgv != argv) {
1883
ckfree((char *) localArgv);
1884
}
1885
1886
/*
1887
*
1888
* NOTE: Need to be careful if the target interpreter and the current
1889
* interpreter are the same - must not destroy result. This may happen
1890
* if an alias is created which redirects to a command in the same
1891
* interpreter as the one in which the source command will be defined.
1892
* Also: We cannot use aliasPtr any more because the alias may have
1893
* been deleted.
1894
*/
1895
1896
if (interp != (Tcl_Interp *) iPtr) {
1897
if (result == TCL_ERROR) {
1898
/*
1899
* An error occurred, so transfer error information from the
1900
* destination interpreter back to our interpreter. Some tricky
1901
* points:
1902
* 1. Must call Tcl_AddErrorInfo in destination interpreter to
1903
* make sure that the errorInfo variable has been initialized
1904
* (it's initialized lazily and might not have been initialized
1905
* yet).
1906
* 2. Must clear interp's result before calling Tcl_AddErrorInfo,
1907
* since Tcl_AddErrorInfo will store the interp's result in
1908
* errorInfo before appending aliasPtr->interp's $errorInfo;
1909
* we've already got everything we need in the redirected
1910
* interpreter's $errorInfo.
1911
*/
1912
1913
if (!(iPtr->flags & ERR_ALREADY_LOGGED)) {
1914
Tcl_AddErrorInfo((Tcl_Interp *) iPtr, "");
1915
}
1916
iPtr->flags &= ~ERR_ALREADY_LOGGED;
1917
Tcl_ResetResult(interp);
1918
Tcl_AddErrorInfo(interp, Tcl_GetVar2((Tcl_Interp *) iPtr,
1919
"errorInfo", (char *) NULL, TCL_GLOBAL_ONLY));
1920
Tcl_SetVar2(interp, "errorCode", (char *) NULL,
1921
Tcl_GetVar2((Tcl_Interp *) iPtr, "errorCode",
1922
(char *) NULL, TCL_GLOBAL_ONLY), TCL_GLOBAL_ONLY);
1923
}
1924
if (iPtr->freeProc != NULL) {
1925
interp->result = iPtr->result;
1926
interp->freeProc = iPtr->freeProc;
1927
iPtr->freeProc = 0;
1928
} else {
1929
Tcl_SetResult(interp, iPtr->result, TCL_VOLATILE);
1930
}
1931
Tcl_ResetResult((Tcl_Interp *) iPtr);
1932
}
1933
Tcl_Release((ClientData) iPtr);
1934
return result;
1935
}
1936
1937
/*
1938
*----------------------------------------------------------------------
1939
*
1940
* AliasCmdDeleteProc --
1941
*
1942
* Is invoked when an alias command is deleted in a slave. Cleans up
1943
* all storage associated with this alias.
1944
*
1945
* Results:
1946
* None.
1947
*
1948
* Side effects:
1949
* Deletes the alias record and its entry in the alias table for
1950
* the interpreter.
1951
*
1952
*----------------------------------------------------------------------
1953
*/
1954
1955
static void
1956
AliasCmdDeleteProc(clientData)
1957
ClientData clientData; /* The alias record for this alias. */
1958
{
1959
Alias *aliasPtr; /* Alias record for alias to delete. */
1960
Target *targetPtr; /* Record for target of this alias. */
1961
int i; /* Loop counter. */
1962
1963
aliasPtr = (Alias *) clientData;
1964
1965
targetPtr = (Target *) Tcl_GetHashValue(aliasPtr->targetEntry);
1966
ckfree((char *) targetPtr);
1967
Tcl_DeleteHashEntry(aliasPtr->targetEntry);
1968
1969
ckfree((char *) aliasPtr->targetName);
1970
ckfree((char *) aliasPtr->aliasName);
1971
for (i = 0; i < aliasPtr->argc; i++) {
1972
ckfree((char *) aliasPtr->argv[i]);
1973
}
1974
if (aliasPtr->argv != (char **) NULL) {
1975
ckfree((char *) aliasPtr->argv);
1976
}
1977
1978
Tcl_DeleteHashEntry(aliasPtr->aliasEntry);
1979
1980
ckfree((char *) aliasPtr);
1981
}
1982
1983
/*
1984
*----------------------------------------------------------------------
1985
*
1986
* MasterRecordDeleteProc -
1987
*
1988
* Is invoked when an interpreter (which is using the "interp" facility)
1989
* is deleted, and it cleans up the storage associated with the
1990
* "tclMasterRecord" assoc-data entry.
1991
*
1992
* Results:
1993
* None.
1994
*
1995
* Side effects:
1996
* Cleans up storage.
1997
*
1998
*----------------------------------------------------------------------
1999
*/
2000
2001
static void
2002
MasterRecordDeleteProc(clientData, interp)
2003
ClientData clientData; /* Master record for deleted interp. */
2004
Tcl_Interp *interp; /* Interpreter being deleted. */
2005
{
2006
Target *targetPtr; /* Loop variable. */
2007
Tcl_HashEntry *hPtr; /* Search element. */
2008
Tcl_HashSearch hSearch; /* Search record (internal). */
2009
Slave *slavePtr; /* Loop variable. */
2010
char *cmdName; /* Name of command to delete. */
2011
Master *masterPtr; /* Interim storage. */
2012
2013
masterPtr = (Master *) clientData;
2014
for (hPtr = Tcl_FirstHashEntry(&(masterPtr->slaveTable), &hSearch);
2015
hPtr != NULL;
2016
hPtr = Tcl_NextHashEntry(&hSearch)) {
2017
slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
2018
cmdName = Tcl_GetCommandName(interp, slavePtr->interpCmd);
2019
(void) Tcl_DeleteCommand(interp, cmdName);
2020
}
2021
Tcl_DeleteHashTable(&(masterPtr->slaveTable));
2022
2023
for (hPtr = Tcl_FirstHashEntry(&(masterPtr->targetTable), &hSearch);
2024
hPtr != NULL;
2025
hPtr = Tcl_FirstHashEntry(&(masterPtr->targetTable), &hSearch)) {
2026
targetPtr = (Target *) Tcl_GetHashValue(hPtr);
2027
cmdName = Tcl_GetCommandName(targetPtr->slaveInterp,
2028
targetPtr->slaveCmd);
2029
(void) Tcl_DeleteCommand(targetPtr->slaveInterp, cmdName);
2030
}
2031
Tcl_DeleteHashTable(&(masterPtr->targetTable));
2032
2033
ckfree((char *) masterPtr);
2034
}
2035
2036
/*
2037
*----------------------------------------------------------------------
2038
*
2039
* SlaveRecordDeleteProc --
2040
*
2041
* Is invoked when an interpreter (which is using the interp facility)
2042
* is deleted, and it cleans up the storage associated with the
2043
* tclSlaveRecord assoc-data entry.
2044
*
2045
* Results:
2046
* None
2047
*
2048
* Side effects:
2049
* Cleans up storage.
2050
*
2051
*----------------------------------------------------------------------
2052
*/
2053
2054
static void
2055
SlaveRecordDeleteProc(clientData, interp)
2056
ClientData clientData; /* Slave record for deleted interp. */
2057
Tcl_Interp *interp; /* Interpreter being deleted. */
2058
{
2059
Slave *slavePtr; /* Interim storage. */
2060
Alias *aliasPtr;
2061
Tcl_HashTable *hTblPtr;
2062
Tcl_HashEntry *hPtr;
2063
Tcl_HashSearch hSearch;
2064
2065
slavePtr = (Slave *) clientData;
2066
2067
/*
2068
* In every case that we call SetAssocData on "tclSlaveRecord",
2069
* slavePtr is not NULL. Otherwise we panic.
2070
*/
2071
2072
if (slavePtr == NULL) {
2073
panic("SlaveRecordDeleteProc: NULL slavePtr");
2074
}
2075
2076
if (slavePtr->interpCmd != (Tcl_Command) NULL) {
2077
Command *cmdPtr = (Command *) slavePtr->interpCmd;
2078
2079
/*
2080
* The interpCmd has not been deleted in the master yet, since
2081
* it's callback sets interpCmd to NULL.
2082
*
2083
* Probably Tcl_DeleteInterp() was called on this interpreter directly,
2084
* rather than via "interp delete", or equivalent (deletion of the
2085
* command in the master).
2086
*
2087
* Perform the cleanup done by SlaveObjectDeleteProc() directly,
2088
* and turn off the callback now (since we are about to free slavePtr
2089
* and this interpreter is going away, while the deletion of commands
2090
* in the master may be deferred).
2091
*/
2092
2093
Tcl_DeleteHashEntry(slavePtr->slaveEntry);
2094
cmdPtr->clientData = NULL;
2095
cmdPtr->deleteProc = NULL;
2096
cmdPtr->deleteData = NULL;
2097
2098
/*
2099
* Get the command name from the master interpreter instead of
2100
* relying on the stored name; the command may have been renamed.
2101
*/
2102
2103
Tcl_DeleteCommand(slavePtr->masterInterp,
2104
Tcl_GetCommandName(slavePtr->masterInterp,
2105
slavePtr->interpCmd));
2106
}
2107
2108
/*
2109
* If there are any aliases, delete those now. This removes any
2110
* dependency on the order of deletion between commands and the
2111
* slave record.
2112
*/
2113
2114
hTblPtr = (Tcl_HashTable *) &(slavePtr->aliasTable);
2115
for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
2116
hPtr != (Tcl_HashEntry *) NULL;
2117
hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch)) {
2118
aliasPtr = (Alias *) Tcl_GetHashValue(hPtr);
2119
2120
/*
2121
* The call to Tcl_DeleteCommand will release the storage
2122
* occuppied by the hash entry and the alias record.
2123
* NOTE that we cannot use the alias name directly because its
2124
* storage will be deleted in the command deletion callback. Hence
2125
* we must use the name for the command as stored in the hash table.
2126
*/
2127
2128
Tcl_DeleteCommand(interp,
2129
Tcl_GetCommandName(interp, aliasPtr->slaveCmd));
2130
}
2131
2132
/*
2133
* Finally dispose of the hash table and the slave record.
2134
*/
2135
2136
Tcl_DeleteHashTable(hTblPtr);
2137
ckfree((char *) slavePtr);
2138
}
2139
2140
/*
2141
*----------------------------------------------------------------------
2142
*
2143
* TclInterpInit --
2144
*
2145
* Initializes the invoking interpreter for using the "interp"
2146
* facility. This is called from inside Tcl_Init.
2147
*
2148
* Results:
2149
* None.
2150
*
2151
* Side effects:
2152
* Adds the "interp" command to an interpreter and initializes several
2153
* records in the associated data of the invoking interpreter.
2154
*
2155
*----------------------------------------------------------------------
2156
*/
2157
2158
int
2159
TclInterpInit(interp)
2160
Tcl_Interp *interp; /* Interpreter to initialize. */
2161
{
2162
Master *masterPtr; /* Its Master record. */
2163
2164
masterPtr = (Master *) ckalloc((unsigned) sizeof(Master));
2165
masterPtr->isSafe = 0;
2166
Tcl_InitHashTable(&(masterPtr->slaveTable), TCL_STRING_KEYS);
2167
Tcl_InitHashTable(&(masterPtr->targetTable), TCL_ONE_WORD_KEYS);
2168
2169
(void) Tcl_SetAssocData(interp, "tclMasterRecord", MasterRecordDeleteProc,
2170
(ClientData) masterPtr);
2171
2172
return TCL_OK;
2173
}
2174
2175
/*
2176
*----------------------------------------------------------------------
2177
*
2178
* Tcl_IsSafe --
2179
*
2180
* Determines whether an interpreter is safe
2181
*
2182
* Results:
2183
* 1 if it is safe, 0 if it is not.
2184
*
2185
* Side effects:
2186
* None.
2187
*
2188
*----------------------------------------------------------------------
2189
*/
2190
2191
int
2192
Tcl_IsSafe(interp)
2193
Tcl_Interp *interp; /* Is this interpreter "safe" ? */
2194
{
2195
Master *masterPtr; /* Its master record. */
2196
2197
if (interp == (Tcl_Interp *) NULL) {
2198
return 0;
2199
}
2200
masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord", NULL);
2201
if (masterPtr == (Master *) NULL) {
2202
panic("Tcl_IsSafe: could not find master record");
2203
}
2204
return masterPtr->isSafe;
2205
}
2206
2207
/*
2208
*----------------------------------------------------------------------
2209
*
2210
* Tcl_MakeSafe --
2211
*
2212
* Makes an interpreter safe.
2213
*
2214
* Results:
2215
* TCL_OK if it succeeds, TCL_ERROR else.
2216
*
2217
* Side effects:
2218
* Removes functionality from an interpreter.
2219
*
2220
*----------------------------------------------------------------------
2221
*/
2222
2223
int
2224
Tcl_MakeSafe(interp)
2225
Tcl_Interp *interp; /* Make this interpreter "safe". */
2226
{
2227
if (interp == (Tcl_Interp *) NULL) {
2228
return TCL_ERROR;
2229
}
2230
return MakeSafe(interp);
2231
}
2232
2233
/*
2234
*----------------------------------------------------------------------
2235
*
2236
* Tcl_CreateSlave --
2237
*
2238
* Creates a slave interpreter. The slavePath argument denotes the
2239
* name of the new slave relative to the current interpreter; the
2240
* slave is a direct descendant of the one-before-last component of
2241
* the path, e.g. it is a descendant of the current interpreter if
2242
* the slavePath argument contains only one component. Optionally makes
2243
* the slave interpreter safe.
2244
*
2245
* Results:
2246
* Returns the interpreter structure created, or NULL if an error
2247
* occurred.
2248
*
2249
* Side effects:
2250
* Creates a new interpreter and a new interpreter object command in
2251
* the interpreter indicated by the slavePath argument.
2252
*
2253
*----------------------------------------------------------------------
2254
*/
2255
2256
Tcl_Interp *
2257
Tcl_CreateSlave(interp, slavePath, isSafe)
2258
Tcl_Interp *interp; /* Interpreter to start search at. */
2259
char *slavePath; /* Name of slave to create. */
2260
int isSafe; /* Should new slave be "safe" ? */
2261
{
2262
if ((interp == (Tcl_Interp *) NULL) || (slavePath == (char *) NULL)) {
2263
return NULL;
2264
}
2265
return CreateSlave(interp, slavePath, isSafe);
2266
}
2267
2268
/*
2269
*----------------------------------------------------------------------
2270
*
2271
* Tcl_GetSlave --
2272
*
2273
* Finds a slave interpreter by its path name.
2274
*
2275
* Results:
2276
* Returns a Tcl_Interp * for the named interpreter or NULL if not
2277
* found.
2278
*
2279
* Side effects:
2280
* None.
2281
*
2282
*----------------------------------------------------------------------
2283
*/
2284
2285
Tcl_Interp *
2286
Tcl_GetSlave(interp, slavePath)
2287
Tcl_Interp *interp; /* Interpreter to start search from. */
2288
char *slavePath; /* Path of slave to find. */
2289
{
2290
Master *masterPtr; /* Interim storage for Master record. */
2291
2292
if ((interp == (Tcl_Interp *) NULL) || (slavePath == (char *) NULL)) {
2293
return NULL;
2294
}
2295
masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord", NULL);
2296
if (masterPtr == (Master *) NULL) {
2297
panic("Tcl_GetSlave: could not find master record");
2298
}
2299
return GetInterp(interp, masterPtr, slavePath, NULL);
2300
}
2301
2302
/*
2303
*----------------------------------------------------------------------
2304
*
2305
* Tcl_GetMaster --
2306
*
2307
* Finds the master interpreter of a slave interpreter.
2308
*
2309
* Results:
2310
* Returns a Tcl_Interp * for the master interpreter or NULL if none.
2311
*
2312
* Side effects:
2313
* None.
2314
*
2315
*----------------------------------------------------------------------
2316
*/
2317
2318
Tcl_Interp *
2319
Tcl_GetMaster(interp)
2320
Tcl_Interp *interp; /* Get the master of this interpreter. */
2321
{
2322
Slave *slavePtr; /* Slave record of this interpreter. */
2323
2324
if (interp == (Tcl_Interp *) NULL) {
2325
return NULL;
2326
}
2327
slavePtr = (Slave *) Tcl_GetAssocData(interp, "tclSlaveRecord", NULL);
2328
if (slavePtr == (Slave *) NULL) {
2329
return NULL;
2330
}
2331
return slavePtr->masterInterp;
2332
}
2333
2334
/*
2335
*----------------------------------------------------------------------
2336
*
2337
* Tcl_CreateAlias --
2338
*
2339
* Creates an alias between two interpreters.
2340
*
2341
* Results:
2342
* TCL_OK if successful, TCL_ERROR if failed. If TCL_ERROR is returned
2343
* the result of slaveInterp will contain an error message.
2344
*
2345
* Side effects:
2346
* Creates a new alias, manipulates the result field of slaveInterp.
2347
*
2348
*----------------------------------------------------------------------
2349
*/
2350
2351
int
2352
Tcl_CreateAlias(slaveInterp, slaveCmd, targetInterp, targetCmd, argc, argv)
2353
Tcl_Interp *slaveInterp; /* Interpreter for source command. */
2354
char *slaveCmd; /* Command to install in slave. */
2355
Tcl_Interp *targetInterp; /* Interpreter for target command. */
2356
char *targetCmd; /* Name of target command. */
2357
int argc; /* How many additional arguments? */
2358
char **argv; /* These are the additional args. */
2359
{
2360
Master *masterPtr; /* Master record for target interp. */
2361
2362
if ((slaveInterp == (Tcl_Interp *) NULL) ||
2363
(targetInterp == (Tcl_Interp *) NULL) ||
2364
(slaveCmd == (char *) NULL) ||
2365
(targetCmd == (char *) NULL)) {
2366
return TCL_ERROR;
2367
}
2368
masterPtr = (Master *) Tcl_GetAssocData(targetInterp, "tclMasterRecord",
2369
NULL);
2370
if (masterPtr == (Master *) NULL) {
2371
panic("Tcl_CreateAlias: could not find master record");
2372
}
2373
return AliasHelper(slaveInterp, slaveInterp, targetInterp, masterPtr,
2374
slaveCmd, targetCmd, argc, argv);
2375
}
2376
2377
/*
2378
*----------------------------------------------------------------------
2379
*
2380
* Tcl_GetAlias --
2381
*
2382
* Gets information about an alias.
2383
*
2384
* Results:
2385
* TCL_OK if successful, TCL_ERROR else. If TCL_ERROR is returned, the
2386
* result field of the interpreter given as argument will contain an
2387
* error message.
2388
*
2389
* Side effects:
2390
* Manipulates the result field of the interpreter given as argument.
2391
*
2392
*----------------------------------------------------------------------
2393
*/
2394
2395
int
2396
Tcl_GetAlias(interp, aliasName, targetInterpPtr, targetNamePtr, argcPtr,
2397
argvPtr)
2398
Tcl_Interp *interp; /* Interp to start search from. */
2399
char *aliasName; /* Name of alias to find. */
2400
Tcl_Interp **targetInterpPtr; /* (Return) target interpreter. */
2401
char **targetNamePtr; /* (Return) name of target command. */
2402
int *argcPtr; /* (Return) count of addnl args. */
2403
char ***argvPtr; /* (Return) additional arguments. */
2404
{
2405
Slave *slavePtr; /* Slave record for slave interp. */
2406
Tcl_HashEntry *hPtr; /* Search element. */
2407
Alias *aliasPtr; /* Storage for alias found. */
2408
2409
if ((interp == (Tcl_Interp *) NULL) || (aliasName == (char *) NULL)) {
2410
return TCL_ERROR;
2411
}
2412
slavePtr = (Slave *) Tcl_GetAssocData(interp, "tclSlaveRecord", NULL);
2413
if (slavePtr == (Slave *) NULL) {
2414
panic("Tcl_GetAlias: could not find slave record");
2415
}
2416
hPtr = Tcl_FindHashEntry(&(slavePtr->aliasTable), aliasName);
2417
if (hPtr == (Tcl_HashEntry *) NULL) {
2418
Tcl_AppendResult(interp, "alias \"", aliasName, "\" not found",
2419
(char *) NULL);
2420
return TCL_ERROR;
2421
}
2422
aliasPtr = (Alias *) Tcl_GetHashValue(hPtr);
2423
if (targetInterpPtr != (Tcl_Interp **) NULL) {
2424
*targetInterpPtr = aliasPtr->targetInterp;
2425
}
2426
if (targetNamePtr != (char **) NULL) {
2427
*targetNamePtr = aliasPtr->targetName;
2428
}
2429
if (argcPtr != (int *) NULL) {
2430
*argcPtr = aliasPtr->argc;
2431
}
2432
if (argvPtr != (char ***) NULL) {
2433
*argvPtr = aliasPtr->argv;
2434
}
2435
return TCL_OK;
2436
}
2437
2438