Parent Directory | Revision Log | Patch
projs/trunk/shared_source/tcl_base/tclbasic.c revision 42 by dashley, Fri Oct 14 01:50:00 2016 UTC | projs/trunk/shared_source/c_tcl_base_7_5_w_mods/tclbasic.c revision 71 by dashley, Sat Nov 5 11:07:06 2016 UTC | |
---|---|---|
# | Line 1 | Line 1 |
/* $Header: /cvsroot/esrg/sfesrg/esrgpcpj/shared/tcl_base/tclbasic.c,v 1.1.1.1 2001/06/13 04:33:43 dtashley Exp $ */ | ||
/* | ||
* tclBasic.c -- | ||
* | ||
* Contains the basic facilities for TCL command interpretation, | ||
* including interpreter creation and deletion, command creation | ||
* and deletion, and command parsing and execution. | ||
* | ||
* Copyright (c) 1987-1994 The Regents of the University of California. | ||
* Copyright (c) 1994-1997 Sun Microsystems, Inc. | ||
* Copyright (c) 1998-1999 by Scriptics Corporation. | ||
* | ||
* See the file "license.terms" for information on usage and redistribution | ||
* of this file, and for a DISCLAIMER OF ALL WARRANTIES. | ||
* | ||
* RCS: @(#) $Id: tclbasic.c,v 1.1.1.1 2001/06/13 04:33:43 dtashley Exp $ | ||
*/ | ||
#include "tclInt.h" | ||
#include "tclCompile.h" | ||
#ifndef TCL_GENERIC_ONLY | ||
# include "tclPort.h" | ||
#endif | ||
/* | ||
* Static procedures in this file: | ||
*/ | ||
static void DeleteInterpProc _ANSI_ARGS_((Tcl_Interp *interp)); | ||
static void ProcessUnexpectedResult _ANSI_ARGS_(( | ||
Tcl_Interp *interp, int returnCode)); | ||
static void RecordTracebackInfo _ANSI_ARGS_(( | ||
Tcl_Interp *interp, Tcl_Obj *objPtr, | ||
int numSrcBytes)); | ||
extern TclStubs tclStubs; | ||
/* | ||
* The following structure defines the commands in the Tcl core. | ||
*/ | ||
typedef struct { | ||
char *name; /* Name of object-based command. */ | ||
Tcl_CmdProc *proc; /* String-based procedure for command. */ | ||
Tcl_ObjCmdProc *objProc; /* Object-based procedure for command. */ | ||
CompileProc *compileProc; /* Procedure called to compile command. */ | ||
int isSafe; /* If non-zero, command will be present | ||
* in safe interpreter. Otherwise it will | ||
* be hidden. */ | ||
} CmdInfo; | ||
/* | ||
* The built-in commands, and the procedures that implement them: | ||
*/ | ||
static CmdInfo builtInCmds[] = { | ||
/* | ||
* Commands in the generic core. Note that at least one of the proc or | ||
* objProc members should be non-NULL. This avoids infinitely recursive | ||
* calls between TclInvokeObjectCommand and TclInvokeStringCommand if a | ||
* command name is computed at runtime and results in the name of a | ||
* compiled command. | ||
*/ | ||
{"append", (Tcl_CmdProc *) NULL, Tcl_AppendObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"array", (Tcl_CmdProc *) NULL, Tcl_ArrayObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"binary", (Tcl_CmdProc *) NULL, Tcl_BinaryObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"break", (Tcl_CmdProc *) NULL, Tcl_BreakObjCmd, | ||
TclCompileBreakCmd, 1}, | ||
{"case", (Tcl_CmdProc *) NULL, Tcl_CaseObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"catch", (Tcl_CmdProc *) NULL, Tcl_CatchObjCmd, | ||
TclCompileCatchCmd, 1}, | ||
{"clock", (Tcl_CmdProc *) NULL, Tcl_ClockObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"concat", (Tcl_CmdProc *) NULL, Tcl_ConcatObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"continue", (Tcl_CmdProc *) NULL, Tcl_ContinueObjCmd, | ||
TclCompileContinueCmd, 1}, | ||
{"encoding", (Tcl_CmdProc *) NULL, Tcl_EncodingObjCmd, | ||
(CompileProc *) NULL, 0}, | ||
{"error", (Tcl_CmdProc *) NULL, Tcl_ErrorObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"eval", (Tcl_CmdProc *) NULL, Tcl_EvalObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"exit", (Tcl_CmdProc *) NULL, Tcl_ExitObjCmd, | ||
(CompileProc *) NULL, 0}, | ||
{"expr", (Tcl_CmdProc *) NULL, Tcl_ExprObjCmd, | ||
TclCompileExprCmd, 1}, | ||
{"fcopy", (Tcl_CmdProc *) NULL, Tcl_FcopyObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"fileevent", (Tcl_CmdProc *) NULL, Tcl_FileEventObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"for", (Tcl_CmdProc *) NULL, Tcl_ForObjCmd, | ||
TclCompileForCmd, 1}, | ||
{"foreach", (Tcl_CmdProc *) NULL, Tcl_ForeachObjCmd, | ||
TclCompileForeachCmd, 1}, | ||
{"format", (Tcl_CmdProc *) NULL, Tcl_FormatObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"global", (Tcl_CmdProc *) NULL, Tcl_GlobalObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"if", (Tcl_CmdProc *) NULL, Tcl_IfObjCmd, | ||
TclCompileIfCmd, 1}, | ||
{"incr", (Tcl_CmdProc *) NULL, Tcl_IncrObjCmd, | ||
TclCompileIncrCmd, 1}, | ||
{"info", (Tcl_CmdProc *) NULL, Tcl_InfoObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"join", (Tcl_CmdProc *) NULL, Tcl_JoinObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"lappend", (Tcl_CmdProc *) NULL, Tcl_LappendObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"lindex", (Tcl_CmdProc *) NULL, Tcl_LindexObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"linsert", (Tcl_CmdProc *) NULL, Tcl_LinsertObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"list", (Tcl_CmdProc *) NULL, Tcl_ListObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"llength", (Tcl_CmdProc *) NULL, Tcl_LlengthObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"load", (Tcl_CmdProc *) NULL, Tcl_LoadObjCmd, | ||
(CompileProc *) NULL, 0}, | ||
{"lrange", (Tcl_CmdProc *) NULL, Tcl_LrangeObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"lreplace", (Tcl_CmdProc *) NULL, Tcl_LreplaceObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"lsearch", (Tcl_CmdProc *) NULL, Tcl_LsearchObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"lsort", (Tcl_CmdProc *) NULL, Tcl_LsortObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"namespace", (Tcl_CmdProc *) NULL, Tcl_NamespaceObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"package", (Tcl_CmdProc *) NULL, Tcl_PackageObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"proc", (Tcl_CmdProc *) NULL, Tcl_ProcObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"regexp", (Tcl_CmdProc *) NULL, Tcl_RegexpObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"regsub", (Tcl_CmdProc *) NULL, Tcl_RegsubObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"rename", (Tcl_CmdProc *) NULL, Tcl_RenameObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"return", (Tcl_CmdProc *) NULL, Tcl_ReturnObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"scan", (Tcl_CmdProc *) NULL, Tcl_ScanObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"set", (Tcl_CmdProc *) NULL, Tcl_SetObjCmd, | ||
TclCompileSetCmd, 1}, | ||
{"split", (Tcl_CmdProc *) NULL, Tcl_SplitObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"string", (Tcl_CmdProc *) NULL, Tcl_StringObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"subst", (Tcl_CmdProc *) NULL, Tcl_SubstObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"switch", (Tcl_CmdProc *) NULL, Tcl_SwitchObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"trace", (Tcl_CmdProc *) NULL, Tcl_TraceObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"unset", (Tcl_CmdProc *) NULL, Tcl_UnsetObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"uplevel", (Tcl_CmdProc *) NULL, Tcl_UplevelObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"upvar", (Tcl_CmdProc *) NULL, Tcl_UpvarObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"variable", (Tcl_CmdProc *) NULL, Tcl_VariableObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"while", (Tcl_CmdProc *) NULL, Tcl_WhileObjCmd, | ||
TclCompileWhileCmd, 1}, | ||
/* | ||
* Commands in the UNIX core: | ||
*/ | ||
#ifndef TCL_GENERIC_ONLY | ||
{"after", (Tcl_CmdProc *) NULL, Tcl_AfterObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"cd", (Tcl_CmdProc *) NULL, Tcl_CdObjCmd, | ||
(CompileProc *) NULL, 0}, | ||
{"close", (Tcl_CmdProc *) NULL, Tcl_CloseObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"eof", (Tcl_CmdProc *) NULL, Tcl_EofObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"fblocked", (Tcl_CmdProc *) NULL, Tcl_FblockedObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"fconfigure", (Tcl_CmdProc *) NULL, Tcl_FconfigureObjCmd, | ||
(CompileProc *) NULL, 0}, | ||
{"file", (Tcl_CmdProc *) NULL, Tcl_FileObjCmd, | ||
(CompileProc *) NULL, 0}, | ||
{"flush", (Tcl_CmdProc *) NULL, Tcl_FlushObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"gets", (Tcl_CmdProc *) NULL, Tcl_GetsObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"glob", (Tcl_CmdProc *) NULL, Tcl_GlobObjCmd, | ||
(CompileProc *) NULL, 0}, | ||
{"open", (Tcl_CmdProc *) NULL, Tcl_OpenObjCmd, | ||
(CompileProc *) NULL, 0}, | ||
{"pid", (Tcl_CmdProc *) NULL, Tcl_PidObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"puts", (Tcl_CmdProc *) NULL, Tcl_PutsObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"pwd", (Tcl_CmdProc *) NULL, Tcl_PwdObjCmd, | ||
(CompileProc *) NULL, 0}, | ||
{"read", (Tcl_CmdProc *) NULL, Tcl_ReadObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"seek", (Tcl_CmdProc *) NULL, Tcl_SeekObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"socket", (Tcl_CmdProc *) NULL, Tcl_SocketObjCmd, | ||
(CompileProc *) NULL, 0}, | ||
{"tell", (Tcl_CmdProc *) NULL, Tcl_TellObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"time", (Tcl_CmdProc *) NULL, Tcl_TimeObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"update", (Tcl_CmdProc *) NULL, Tcl_UpdateObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"vwait", (Tcl_CmdProc *) NULL, Tcl_VwaitObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
#ifdef MAC_TCL | ||
{"beep", (Tcl_CmdProc *) NULL, Tcl_BeepObjCmd, | ||
(CompileProc *) NULL, 0}, | ||
{"echo", Tcl_EchoCmd, (Tcl_ObjCmdProc *) NULL, | ||
(CompileProc *) NULL, 0}, | ||
{"ls", (Tcl_CmdProc *) NULL, Tcl_LsObjCmd, | ||
(CompileProc *) NULL, 0}, | ||
{"resource", (Tcl_CmdProc *) NULL, Tcl_ResourceObjCmd, | ||
(CompileProc *) NULL, 1}, | ||
{"source", (Tcl_CmdProc *) NULL, Tcl_MacSourceObjCmd, | ||
(CompileProc *) NULL, 0}, | ||
#else | ||
{"exec", (Tcl_CmdProc *) NULL, Tcl_ExecObjCmd, | ||
(CompileProc *) NULL, 0}, | ||
{"source", (Tcl_CmdProc *) NULL, Tcl_SourceObjCmd, | ||
(CompileProc *) NULL, 0}, | ||
#endif /* MAC_TCL */ | ||
#endif /* TCL_GENERIC_ONLY */ | ||
{NULL, (Tcl_CmdProc *) NULL, (Tcl_ObjCmdProc *) NULL, | ||
(CompileProc *) NULL, 0} | ||
}; | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_CreateInterp -- | ||
* | ||
* Create a new TCL command interpreter. | ||
* | ||
* Results: | ||
* The return value is a token for the interpreter, which may be | ||
* used in calls to procedures like Tcl_CreateCmd, Tcl_Eval, or | ||
* Tcl_DeleteInterp. | ||
* | ||
* Side effects: | ||
* The command interpreter is initialized with an empty variable | ||
* table and the built-in commands. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
Tcl_Interp * | ||
Tcl_CreateInterp() | ||
{ | ||
Interp *iPtr; | ||
Tcl_Interp *interp; | ||
Command *cmdPtr; | ||
BuiltinFunc *builtinFuncPtr; | ||
MathFunc *mathFuncPtr; | ||
Tcl_HashEntry *hPtr; | ||
CmdInfo *cmdInfoPtr; | ||
int i; | ||
union { | ||
char c[sizeof(short)]; | ||
short s; | ||
} order; | ||
#ifdef TCL_COMPILE_STATS | ||
ByteCodeStats *statsPtr; | ||
#endif /* TCL_COMPILE_STATS */ | ||
TclInitSubsystems(NULL); | ||
/* | ||
* Panic if someone updated the CallFrame structure without | ||
* also updating the Tcl_CallFrame structure (or vice versa). | ||
*/ | ||
if (sizeof(Tcl_CallFrame) != sizeof(CallFrame)) { | ||
/*NOTREACHED*/ | ||
panic("Tcl_CallFrame and CallFrame are not the same size"); | ||
} | ||
/* | ||
* Initialize support for namespaces and create the global namespace | ||
* (whose name is ""; an alias is "::"). This also initializes the | ||
* Tcl object type table and other object management code. | ||
*/ | ||
iPtr = (Interp *) ckalloc(sizeof(Interp)); | ||
interp = (Tcl_Interp *) iPtr; | ||
iPtr->result = iPtr->resultSpace; | ||
iPtr->freeProc = NULL; | ||
iPtr->errorLine = 0; | ||
iPtr->objResultPtr = Tcl_NewObj(); | ||
Tcl_IncrRefCount(iPtr->objResultPtr); | ||
iPtr->handle = TclHandleCreate(iPtr); | ||
iPtr->globalNsPtr = NULL; | ||
iPtr->hiddenCmdTablePtr = NULL; | ||
iPtr->interpInfo = NULL; | ||
Tcl_InitHashTable(&iPtr->mathFuncTable, TCL_STRING_KEYS); | ||
iPtr->numLevels = 0; | ||
iPtr->maxNestingDepth = 1000; | ||
iPtr->framePtr = NULL; | ||
iPtr->varFramePtr = NULL; | ||
iPtr->activeTracePtr = NULL; | ||
iPtr->returnCode = TCL_OK; | ||
iPtr->errorInfo = NULL; | ||
iPtr->errorCode = NULL; | ||
iPtr->appendResult = NULL; | ||
iPtr->appendAvl = 0; | ||
iPtr->appendUsed = 0; | ||
Tcl_InitHashTable(&iPtr->packageTable, TCL_STRING_KEYS); | ||
iPtr->packageUnknown = NULL; | ||
iPtr->cmdCount = 0; | ||
iPtr->termOffset = 0; | ||
TclInitLiteralTable(&(iPtr->literalTable)); | ||
iPtr->compileEpoch = 0; | ||
iPtr->compiledProcPtr = NULL; | ||
iPtr->resolverPtr = NULL; | ||
iPtr->evalFlags = 0; | ||
iPtr->scriptFile = NULL; | ||
iPtr->flags = 0; | ||
iPtr->tracePtr = NULL; | ||
iPtr->assocData = (Tcl_HashTable *) NULL; | ||
iPtr->execEnvPtr = NULL; /* set after namespaces initialized */ | ||
iPtr->emptyObjPtr = Tcl_NewObj(); /* another empty object */ | ||
Tcl_IncrRefCount(iPtr->emptyObjPtr); | ||
iPtr->resultSpace[0] = 0; | ||
iPtr->globalNsPtr = NULL; /* force creation of global ns below */ | ||
iPtr->globalNsPtr = (Namespace *) Tcl_CreateNamespace(interp, "", | ||
(ClientData) NULL, (Tcl_NamespaceDeleteProc *) NULL); | ||
if (iPtr->globalNsPtr == NULL) { | ||
panic("Tcl_CreateInterp: can't create global namespace"); | ||
} | ||
/* | ||
* Initialize support for code compilation and execution. We call | ||
* TclCreateExecEnv after initializing namespaces since it tries to | ||
* reference a Tcl variable (it links to the Tcl "tcl_traceExec" | ||
* variable). | ||
*/ | ||
iPtr->execEnvPtr = TclCreateExecEnv(interp); | ||
/* | ||
* Initialize the compilation and execution statistics kept for this | ||
* interpreter. | ||
*/ | ||
#ifdef TCL_COMPILE_STATS | ||
statsPtr = &(iPtr->stats); | ||
statsPtr->numExecutions = 0; | ||
statsPtr->numCompilations = 0; | ||
statsPtr->numByteCodesFreed = 0; | ||
(VOID *) memset(statsPtr->instructionCount, 0, | ||
sizeof(statsPtr->instructionCount)); | ||
statsPtr->totalSrcBytes = 0.0; | ||
statsPtr->totalByteCodeBytes = 0.0; | ||
statsPtr->currentSrcBytes = 0.0; | ||
statsPtr->currentByteCodeBytes = 0.0; | ||
(VOID *) memset(statsPtr->srcCount, 0, sizeof(statsPtr->srcCount)); | ||
(VOID *) memset(statsPtr->byteCodeCount, 0, | ||
sizeof(statsPtr->byteCodeCount)); | ||
(VOID *) memset(statsPtr->lifetimeCount, 0, | ||
sizeof(statsPtr->lifetimeCount)); | ||
statsPtr->currentInstBytes = 0.0; | ||
statsPtr->currentLitBytes = 0.0; | ||
statsPtr->currentExceptBytes = 0.0; | ||
statsPtr->currentAuxBytes = 0.0; | ||
statsPtr->currentCmdMapBytes = 0.0; | ||
statsPtr->numLiteralsCreated = 0; | ||
statsPtr->totalLitStringBytes = 0.0; | ||
statsPtr->currentLitStringBytes = 0.0; | ||
(VOID *) memset(statsPtr->literalCount, 0, | ||
sizeof(statsPtr->literalCount)); | ||
#endif /* TCL_COMPILE_STATS */ | ||
/* | ||
* Initialise the stub table pointer. | ||
*/ | ||
iPtr->stubTable = &tclStubs; | ||
/* | ||
* Create the core commands. Do it here, rather than calling | ||
* Tcl_CreateCommand, because it's faster (there's no need to check for | ||
* a pre-existing command by the same name). If a command has a | ||
* Tcl_CmdProc but no Tcl_ObjCmdProc, set the Tcl_ObjCmdProc to | ||
* TclInvokeStringCommand. This is an object-based wrapper procedure | ||
* that extracts strings, calls the string procedure, and creates an | ||
* object for the result. Similarly, if a command has a Tcl_ObjCmdProc | ||
* but no Tcl_CmdProc, set the Tcl_CmdProc to TclInvokeObjectCommand. | ||
*/ | ||
for (cmdInfoPtr = builtInCmds; cmdInfoPtr->name != NULL; | ||
cmdInfoPtr++) { | ||
int new; | ||
Tcl_HashEntry *hPtr; | ||
if ((cmdInfoPtr->proc == (Tcl_CmdProc *) NULL) | ||
&& (cmdInfoPtr->objProc == (Tcl_ObjCmdProc *) NULL) | ||
&& (cmdInfoPtr->compileProc == (CompileProc *) NULL)) { | ||
panic("Tcl_CreateInterp: builtin command with NULL string and object command procs and a NULL compile proc\n"); | ||
} | ||
hPtr = Tcl_CreateHashEntry(&iPtr->globalNsPtr->cmdTable, | ||
cmdInfoPtr->name, &new); | ||
if (new) { | ||
cmdPtr = (Command *) ckalloc(sizeof(Command)); | ||
cmdPtr->hPtr = hPtr; | ||
cmdPtr->nsPtr = iPtr->globalNsPtr; | ||
cmdPtr->refCount = 1; | ||
cmdPtr->cmdEpoch = 0; | ||
cmdPtr->compileProc = cmdInfoPtr->compileProc; | ||
if (cmdInfoPtr->proc == (Tcl_CmdProc *) NULL) { | ||
cmdPtr->proc = TclInvokeObjectCommand; | ||
cmdPtr->clientData = (ClientData) cmdPtr; | ||
} else { | ||
cmdPtr->proc = cmdInfoPtr->proc; | ||
cmdPtr->clientData = (ClientData) NULL; | ||
} | ||
if (cmdInfoPtr->objProc == (Tcl_ObjCmdProc *) NULL) { | ||
cmdPtr->objProc = TclInvokeStringCommand; | ||
cmdPtr->objClientData = (ClientData) cmdPtr; | ||
} else { | ||
cmdPtr->objProc = cmdInfoPtr->objProc; | ||
cmdPtr->objClientData = (ClientData) NULL; | ||
} | ||
cmdPtr->deleteProc = NULL; | ||
cmdPtr->deleteData = (ClientData) NULL; | ||
cmdPtr->deleted = 0; | ||
cmdPtr->importRefPtr = NULL; | ||
Tcl_SetHashValue(hPtr, cmdPtr); | ||
} | ||
} | ||
/* | ||
* Register the builtin math functions. | ||
*/ | ||
i = 0; | ||
for (builtinFuncPtr = builtinFuncTable; builtinFuncPtr->name != NULL; | ||
builtinFuncPtr++) { | ||
Tcl_CreateMathFunc((Tcl_Interp *) iPtr, builtinFuncPtr->name, | ||
builtinFuncPtr->numArgs, builtinFuncPtr->argTypes, | ||
(Tcl_MathProc *) NULL, (ClientData) 0); | ||
hPtr = Tcl_FindHashEntry(&iPtr->mathFuncTable, | ||
builtinFuncPtr->name); | ||
if (hPtr == NULL) { | ||
panic("Tcl_CreateInterp: Tcl_CreateMathFunc incorrectly registered '%s'", builtinFuncPtr->name); | ||
return NULL; | ||
} | ||
mathFuncPtr = (MathFunc *) Tcl_GetHashValue(hPtr); | ||
mathFuncPtr->builtinFuncIndex = i; | ||
i++; | ||
} | ||
iPtr->flags |= EXPR_INITIALIZED; | ||
/* | ||
* Do Multiple/Safe Interps Tcl init stuff | ||
*/ | ||
TclInterpInit(interp); | ||
/* | ||
* We used to create the "errorInfo" and "errorCode" global vars at this | ||
* point because so much of the Tcl implementation assumes they already | ||
* exist. This is not quite enough, however, since they can be unset | ||
* at any time. | ||
* | ||
* There are 2 choices: | ||
* + Check every place where a GetVar of those is used | ||
* and the NULL result is not checked (like in tclLoad.c) | ||
* + Make SetVar,... NULL friendly | ||
* We choose the second option because : | ||
* + It is easy and low cost to check for NULL pointer before | ||
* calling strlen() | ||
* + It can be helpfull to other people using those API | ||
* + Passing a NULL value to those closest 'meaning' is empty string | ||
* (specially with the new objects where 0 bytes strings are ok) | ||
* So the following init is commented out: -- dl | ||
* | ||
* (void) Tcl_SetVar2((Tcl_Interp *)iPtr, "errorInfo", (char *) NULL, | ||
* "", TCL_GLOBAL_ONLY); | ||
* (void) Tcl_SetVar2((Tcl_Interp *)iPtr, "errorCode", (char *) NULL, | ||
* "NONE", TCL_GLOBAL_ONLY); | ||
*/ | ||
#ifndef TCL_GENERIC_ONLY | ||
TclSetupEnv(interp); | ||
#endif | ||
/* | ||
* Compute the byte order of this machine. | ||
*/ | ||
order.s = 1; | ||
Tcl_SetVar2(interp, "tcl_platform", "byteOrder", | ||
((order.c[0] == 1) ? "littleEndian" : "bigEndian"), | ||
TCL_GLOBAL_ONLY); | ||
/* | ||
* Set up other variables such as tcl_version and tcl_library | ||
*/ | ||
Tcl_SetVar(interp, "tcl_patchLevel", TCL_PATCH_LEVEL, TCL_GLOBAL_ONLY); | ||
Tcl_SetVar(interp, "tcl_version", TCL_VERSION, TCL_GLOBAL_ONLY); | ||
Tcl_TraceVar2(interp, "tcl_precision", (char *) NULL, | ||
TCL_GLOBAL_ONLY|TCL_TRACE_READS|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | ||
TclPrecTraceProc, (ClientData) NULL); | ||
TclpSetVariables(interp); | ||
#ifdef TCL_THREADS | ||
/* | ||
* The existence of the "threaded" element of the tcl_platform array indicates | ||
* that this particular Tcl shell has been compiled with threads turned on. | ||
* Using "info exists tcl_platform(threaded)" a Tcl script can introspect on the | ||
* interpreter level of thread safety. | ||
*/ | ||
Tcl_SetVar2(interp, "tcl_platform", "threaded", "1", | ||
TCL_GLOBAL_ONLY); | ||
#endif | ||
/* | ||
* Register Tcl's version number. | ||
*/ | ||
Tcl_PkgProvideEx(interp, "Tcl", TCL_VERSION, (ClientData) &tclStubs); | ||
#ifdef Tcl_InitStubs | ||
#undef Tcl_InitStubs | ||
#endif | ||
Tcl_InitStubs(interp, TCL_VERSION, 1); | ||
return interp; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* TclHideUnsafeCommands -- | ||
* | ||
* Hides base commands that are not marked as safe from this | ||
* interpreter. | ||
* | ||
* Results: | ||
* TCL_OK if it succeeds, TCL_ERROR else. | ||
* | ||
* Side effects: | ||
* Hides functionality in an interpreter. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
int | ||
TclHideUnsafeCommands(interp) | ||
Tcl_Interp *interp; /* Hide commands in this interpreter. */ | ||
{ | ||
register CmdInfo *cmdInfoPtr; | ||
if (interp == (Tcl_Interp *) NULL) { | ||
return TCL_ERROR; | ||
} | ||
for (cmdInfoPtr = builtInCmds; cmdInfoPtr->name != NULL; cmdInfoPtr++) { | ||
if (!cmdInfoPtr->isSafe) { | ||
Tcl_HideCommand(interp, cmdInfoPtr->name, cmdInfoPtr->name); | ||
} | ||
} | ||
return TCL_OK; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* Tcl_CallWhenDeleted -- | ||
* | ||
* Arrange for a procedure to be called before a given | ||
* interpreter is deleted. The procedure is called as soon | ||
* as Tcl_DeleteInterp is called; if Tcl_CallWhenDeleted is | ||
* called on an interpreter that has already been deleted, | ||
* the procedure will be called when the last Tcl_Release is | ||
* done on the interpreter. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* When Tcl_DeleteInterp is invoked to delete interp, | ||
* proc will be invoked. See the manual entry for | ||
* details. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
void | ||
Tcl_CallWhenDeleted(interp, proc, clientData) | ||
Tcl_Interp *interp; /* Interpreter to watch. */ | ||
Tcl_InterpDeleteProc *proc; /* Procedure to call when interpreter | ||
* is about to be deleted. */ | ||
ClientData clientData; /* One-word value to pass to proc. */ | ||
{ | ||
Interp *iPtr = (Interp *) interp; | ||
static int assocDataCounter = 0; | ||
#ifdef TCL_THREADS | ||
static Tcl_Mutex assocMutex; | ||
#endif | ||
int new; | ||
char buffer[32 + TCL_INTEGER_SPACE]; | ||
AssocData *dPtr = (AssocData *) ckalloc(sizeof(AssocData)); | ||
Tcl_HashEntry *hPtr; | ||
Tcl_MutexLock(&assocMutex); | ||
sprintf(buffer, "Assoc Data Key #%d", assocDataCounter); | ||
assocDataCounter++; | ||
Tcl_MutexUnlock(&assocMutex); | ||
if (iPtr->assocData == (Tcl_HashTable *) NULL) { | ||
iPtr->assocData = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable)); | ||
Tcl_InitHashTable(iPtr->assocData, TCL_STRING_KEYS); | ||
} | ||
hPtr = Tcl_CreateHashEntry(iPtr->assocData, buffer, &new); | ||
dPtr->proc = proc; | ||
dPtr->clientData = clientData; | ||
Tcl_SetHashValue(hPtr, dPtr); | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* Tcl_DontCallWhenDeleted -- | ||
* | ||
* Cancel the arrangement for a procedure to be called when | ||
* a given interpreter is deleted. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* If proc and clientData were previously registered as a | ||
* callback via Tcl_CallWhenDeleted, they are unregistered. | ||
* If they weren't previously registered then nothing | ||
* happens. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
void | ||
Tcl_DontCallWhenDeleted(interp, proc, clientData) | ||
Tcl_Interp *interp; /* Interpreter to watch. */ | ||
Tcl_InterpDeleteProc *proc; /* Procedure to call when interpreter | ||
* is about to be deleted. */ | ||
ClientData clientData; /* One-word value to pass to proc. */ | ||
{ | ||
Interp *iPtr = (Interp *) interp; | ||
Tcl_HashTable *hTablePtr; | ||
Tcl_HashSearch hSearch; | ||
Tcl_HashEntry *hPtr; | ||
AssocData *dPtr; | ||
hTablePtr = iPtr->assocData; | ||
if (hTablePtr == (Tcl_HashTable *) NULL) { | ||
return; | ||
} | ||
for (hPtr = Tcl_FirstHashEntry(hTablePtr, &hSearch); hPtr != NULL; | ||
hPtr = Tcl_NextHashEntry(&hSearch)) { | ||
dPtr = (AssocData *) Tcl_GetHashValue(hPtr); | ||
if ((dPtr->proc == proc) && (dPtr->clientData == clientData)) { | ||
ckfree((char *) dPtr); | ||
Tcl_DeleteHashEntry(hPtr); | ||
return; | ||
} | ||
} | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_SetAssocData -- | ||
* | ||
* Creates a named association between user-specified data, a delete | ||
* function and this interpreter. If the association already exists | ||
* the data is overwritten with the new data. The delete function will | ||
* be invoked when the interpreter is deleted. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* Sets the associated data, creates the association if needed. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
void | ||
Tcl_SetAssocData(interp, name, proc, clientData) | ||
Tcl_Interp *interp; /* Interpreter to associate with. */ | ||
char *name; /* Name for association. */ | ||
Tcl_InterpDeleteProc *proc; /* Proc to call when interpreter is | ||
* about to be deleted. */ | ||
ClientData clientData; /* One-word value to pass to proc. */ | ||
{ | ||
Interp *iPtr = (Interp *) interp; | ||
AssocData *dPtr; | ||
Tcl_HashEntry *hPtr; | ||
int new; | ||
if (iPtr->assocData == (Tcl_HashTable *) NULL) { | ||
iPtr->assocData = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable)); | ||
Tcl_InitHashTable(iPtr->assocData, TCL_STRING_KEYS); | ||
} | ||
hPtr = Tcl_CreateHashEntry(iPtr->assocData, name, &new); | ||
if (new == 0) { | ||
dPtr = (AssocData *) Tcl_GetHashValue(hPtr); | ||
} else { | ||
dPtr = (AssocData *) ckalloc(sizeof(AssocData)); | ||
} | ||
dPtr->proc = proc; | ||
dPtr->clientData = clientData; | ||
Tcl_SetHashValue(hPtr, dPtr); | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_DeleteAssocData -- | ||
* | ||
* Deletes a named association of user-specified data with | ||
* the specified interpreter. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* Deletes the association. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
void | ||
Tcl_DeleteAssocData(interp, name) | ||
Tcl_Interp *interp; /* Interpreter to associate with. */ | ||
char *name; /* Name of association. */ | ||
{ | ||
Interp *iPtr = (Interp *) interp; | ||
AssocData *dPtr; | ||
Tcl_HashEntry *hPtr; | ||
if (iPtr->assocData == (Tcl_HashTable *) NULL) { | ||
return; | ||
} | ||
hPtr = Tcl_FindHashEntry(iPtr->assocData, name); | ||
if (hPtr == (Tcl_HashEntry *) NULL) { | ||
return; | ||
} | ||
dPtr = (AssocData *) Tcl_GetHashValue(hPtr); | ||
if (dPtr->proc != NULL) { | ||
(dPtr->proc) (dPtr->clientData, interp); | ||
} | ||
ckfree((char *) dPtr); | ||
Tcl_DeleteHashEntry(hPtr); | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_GetAssocData -- | ||
* | ||
* Returns the client data associated with this name in the | ||
* specified interpreter. | ||
* | ||
* Results: | ||
* The client data in the AssocData record denoted by the named | ||
* association, or NULL. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
ClientData | ||
Tcl_GetAssocData(interp, name, procPtr) | ||
Tcl_Interp *interp; /* Interpreter associated with. */ | ||
char *name; /* Name of association. */ | ||
Tcl_InterpDeleteProc **procPtr; /* Pointer to place to store address | ||
* of current deletion callback. */ | ||
{ | ||
Interp *iPtr = (Interp *) interp; | ||
AssocData *dPtr; | ||
Tcl_HashEntry *hPtr; | ||
if (iPtr->assocData == (Tcl_HashTable *) NULL) { | ||
return (ClientData) NULL; | ||
} | ||
hPtr = Tcl_FindHashEntry(iPtr->assocData, name); | ||
if (hPtr == (Tcl_HashEntry *) NULL) { | ||
return (ClientData) NULL; | ||
} | ||
dPtr = (AssocData *) Tcl_GetHashValue(hPtr); | ||
if (procPtr != (Tcl_InterpDeleteProc **) NULL) { | ||
*procPtr = dPtr->proc; | ||
} | ||
return dPtr->clientData; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_InterpDeleted -- | ||
* | ||
* Returns nonzero if the interpreter has been deleted with a call | ||
* to Tcl_DeleteInterp. | ||
* | ||
* Results: | ||
* Nonzero if the interpreter is deleted, zero otherwise. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
int | ||
Tcl_InterpDeleted(interp) | ||
Tcl_Interp *interp; | ||
{ | ||
return (((Interp *) interp)->flags & DELETED) ? 1 : 0; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_DeleteInterp -- | ||
* | ||
* Ensures that the interpreter will be deleted eventually. If there | ||
* are no Tcl_Preserve calls in effect for this interpreter, it is | ||
* deleted immediately, otherwise the interpreter is deleted when | ||
* the last Tcl_Preserve is matched by a call to Tcl_Release. In either | ||
* case, the procedure runs the currently registered deletion callbacks. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* The interpreter is marked as deleted. The caller may still use it | ||
* safely if there are calls to Tcl_Preserve in effect for the | ||
* interpreter, but further calls to Tcl_Eval etc in this interpreter | ||
* will fail. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
void | ||
Tcl_DeleteInterp(interp) | ||
Tcl_Interp *interp; /* Token for command interpreter (returned | ||
* by a previous call to Tcl_CreateInterp). */ | ||
{ | ||
Interp *iPtr = (Interp *) interp; | ||
/* | ||
* If the interpreter has already been marked deleted, just punt. | ||
*/ | ||
if (iPtr->flags & DELETED) { | ||
return; | ||
} | ||
/* | ||
* Mark the interpreter as deleted. No further evals will be allowed. | ||
*/ | ||
iPtr->flags |= DELETED; | ||
/* | ||
* Ensure that the interpreter is eventually deleted. | ||
*/ | ||
Tcl_EventuallyFree((ClientData) interp, | ||
(Tcl_FreeProc *) DeleteInterpProc); | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* DeleteInterpProc -- | ||
* | ||
* Helper procedure to delete an interpreter. This procedure is | ||
* called when the last call to Tcl_Preserve on this interpreter | ||
* is matched by a call to Tcl_Release. The procedure cleans up | ||
* all resources used in the interpreter and calls all currently | ||
* registered interpreter deletion callbacks. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* Whatever the interpreter deletion callbacks do. Frees resources | ||
* used by the interpreter. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static void | ||
DeleteInterpProc(interp) | ||
Tcl_Interp *interp; /* Interpreter to delete. */ | ||
{ | ||
Interp *iPtr = (Interp *) interp; | ||
Tcl_HashEntry *hPtr; | ||
Tcl_HashSearch search; | ||
Tcl_HashTable *hTablePtr; | ||
ResolverScheme *resPtr, *nextResPtr; | ||
/* | ||
* Punt if there is an error in the Tcl_Release/Tcl_Preserve matchup. | ||
*/ | ||
if (iPtr->numLevels > 0) { | ||
panic("DeleteInterpProc called with active evals"); | ||
} | ||
/* | ||
* The interpreter should already be marked deleted; otherwise how | ||
* did we get here? | ||
*/ | ||
if (!(iPtr->flags & DELETED)) { | ||
panic("DeleteInterpProc called on interpreter not marked deleted"); | ||
} | ||
TclHandleFree(iPtr->handle); | ||
/* | ||
* Dismantle everything in the global namespace except for the | ||
* "errorInfo" and "errorCode" variables. These remain until the | ||
* namespace is actually destroyed, in case any errors occur. | ||
* | ||
* Dismantle the namespace here, before we clear the assocData. If any | ||
* background errors occur here, they will be deleted below. | ||
*/ | ||
TclTeardownNamespace(iPtr->globalNsPtr); | ||
/* | ||
* Delete all the hidden commands. | ||
*/ | ||
hTablePtr = iPtr->hiddenCmdTablePtr; | ||
if (hTablePtr != NULL) { | ||
/* | ||
* Non-pernicious deletion. The deletion callbacks will not be | ||
* allowed to create any new hidden or non-hidden commands. | ||
* Tcl_DeleteCommandFromToken() will remove the entry from the | ||
* hiddenCmdTablePtr. | ||
*/ | ||
hPtr = Tcl_FirstHashEntry(hTablePtr, &search); | ||
for ( ; hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { | ||
Tcl_DeleteCommandFromToken(interp, | ||
(Tcl_Command) Tcl_GetHashValue(hPtr)); | ||
} | ||
Tcl_DeleteHashTable(hTablePtr); | ||
ckfree((char *) hTablePtr); | ||
} | ||
/* | ||
* Tear down the math function table. | ||
*/ | ||
for (hPtr = Tcl_FirstHashEntry(&iPtr->mathFuncTable, &search); | ||
hPtr != NULL; | ||
hPtr = Tcl_NextHashEntry(&search)) { | ||
ckfree((char *) Tcl_GetHashValue(hPtr)); | ||
} | ||
Tcl_DeleteHashTable(&iPtr->mathFuncTable); | ||
/* | ||
* Invoke deletion callbacks; note that a callback can create new | ||
* callbacks, so we iterate. | ||
*/ | ||
while (iPtr->assocData != (Tcl_HashTable *) NULL) { | ||
AssocData *dPtr; | ||
hTablePtr = iPtr->assocData; | ||
iPtr->assocData = (Tcl_HashTable *) NULL; | ||
for (hPtr = Tcl_FirstHashEntry(hTablePtr, &search); | ||
hPtr != NULL; | ||
hPtr = Tcl_FirstHashEntry(hTablePtr, &search)) { | ||
dPtr = (AssocData *) Tcl_GetHashValue(hPtr); | ||
Tcl_DeleteHashEntry(hPtr); | ||
if (dPtr->proc != NULL) { | ||
(*dPtr->proc)(dPtr->clientData, interp); | ||
} | ||
ckfree((char *) dPtr); | ||
} | ||
Tcl_DeleteHashTable(hTablePtr); | ||
ckfree((char *) hTablePtr); | ||
} | ||
/* | ||
* Finish deleting the global namespace. | ||
*/ | ||
Tcl_DeleteNamespace((Tcl_Namespace *) iPtr->globalNsPtr); | ||
/* | ||
* Free up the result *after* deleting variables, since variable | ||
* deletion could have transferred ownership of the result string | ||
* to Tcl. | ||
*/ | ||
Tcl_FreeResult(interp); | ||
interp->result = NULL; | ||
Tcl_DecrRefCount(iPtr->objResultPtr); | ||
iPtr->objResultPtr = NULL; | ||
if (iPtr->errorInfo != NULL) { | ||
ckfree(iPtr->errorInfo); | ||
iPtr->errorInfo = NULL; | ||
} | ||
if (iPtr->errorCode != NULL) { | ||
ckfree(iPtr->errorCode); | ||
iPtr->errorCode = NULL; | ||
} | ||
if (iPtr->appendResult != NULL) { | ||
ckfree(iPtr->appendResult); | ||
iPtr->appendResult = NULL; | ||
} | ||
TclFreePackageInfo(iPtr); | ||
while (iPtr->tracePtr != NULL) { | ||
Trace *nextPtr = iPtr->tracePtr->nextPtr; | ||
ckfree((char *) iPtr->tracePtr); | ||
iPtr->tracePtr = nextPtr; | ||
} | ||
if (iPtr->execEnvPtr != NULL) { | ||
TclDeleteExecEnv(iPtr->execEnvPtr); | ||
} | ||
Tcl_DecrRefCount(iPtr->emptyObjPtr); | ||
iPtr->emptyObjPtr = NULL; | ||
resPtr = iPtr->resolverPtr; | ||
while (resPtr) { | ||
nextResPtr = resPtr->nextPtr; | ||
ckfree(resPtr->name); | ||
ckfree((char *) resPtr); | ||
resPtr = nextResPtr; | ||
} | ||
/* | ||
* Free up literal objects created for scripts compiled by the | ||
* interpreter. | ||
*/ | ||
TclDeleteLiteralTable(interp, &(iPtr->literalTable)); | ||
ckfree((char *) iPtr); | ||
} | ||
/* | ||
*--------------------------------------------------------------------------- | ||
* | ||
* Tcl_HideCommand -- | ||
* | ||
* Makes a command hidden so that it cannot be invoked from within | ||
* an interpreter, only from within an ancestor. | ||
* | ||
* Results: | ||
* A standard Tcl result; also leaves a message in the interp's result | ||
* if an error occurs. | ||
* | ||
* Side effects: | ||
* Removes a command from the command table and create an entry | ||
* into the hidden command table under the specified token name. | ||
* | ||
*--------------------------------------------------------------------------- | ||
*/ | ||
int | ||
Tcl_HideCommand(interp, cmdName, hiddenCmdToken) | ||
Tcl_Interp *interp; /* Interpreter in which to hide command. */ | ||
char *cmdName; /* Name of command to hide. */ | ||
char *hiddenCmdToken; /* Token name of the to-be-hidden command. */ | ||
{ | ||
Interp *iPtr = (Interp *) interp; | ||
Tcl_Command cmd; | ||
Command *cmdPtr; | ||
Tcl_HashTable *hiddenCmdTablePtr; | ||
Tcl_HashEntry *hPtr; | ||
int new; | ||
if (iPtr->flags & DELETED) { | ||
/* | ||
* The interpreter is being deleted. Do not create any new | ||
* structures, because it is not safe to modify the interpreter. | ||
*/ | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* Disallow hiding of commands that are currently in a namespace or | ||
* renaming (as part of hiding) into a namespace. | ||
* | ||
* (because the current implementation with a single global table | ||
* and the needed uniqueness of names cause problems with namespaces) | ||
* | ||
* we don't need to check for "::" in cmdName because the real check is | ||
* on the nsPtr below. | ||
* | ||
* hiddenCmdToken is just a string which is not interpreted in any way. | ||
* It may contain :: but the string is not interpreted as a namespace | ||
* qualifier command name. Thus, hiding foo::bar to foo::bar and then | ||
* trying to expose or invoke ::foo::bar will NOT work; but if the | ||
* application always uses the same strings it will get consistent | ||
* behaviour. | ||
* | ||
* But as we currently limit ourselves to the global namespace only | ||
* for the source, in order to avoid potential confusion, | ||
* lets prevent "::" in the token too. --dl | ||
*/ | ||
if (strstr(hiddenCmdToken, "::") != NULL) { | ||
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), | ||
"cannot use namespace qualifiers as hidden command", | ||
"token (rename)", (char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* Find the command to hide. An error is returned if cmdName can't | ||
* be found. Look up the command only from the global namespace. | ||
* Full path of the command must be given if using namespaces. | ||
*/ | ||
cmd = Tcl_FindCommand(interp, cmdName, (Tcl_Namespace *) NULL, | ||
/*flags*/ TCL_LEAVE_ERR_MSG | TCL_GLOBAL_ONLY); | ||
if (cmd == (Tcl_Command) NULL) { | ||
return TCL_ERROR; | ||
} | ||
cmdPtr = (Command *) cmd; | ||
/* | ||
* Check that the command is really in global namespace | ||
*/ | ||
if ( cmdPtr->nsPtr != iPtr->globalNsPtr ) { | ||
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), | ||
"can only hide global namespace commands", | ||
" (use rename then hide)", (char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* Initialize the hidden command table if necessary. | ||
*/ | ||
hiddenCmdTablePtr = iPtr->hiddenCmdTablePtr; | ||
if (hiddenCmdTablePtr == NULL) { | ||
hiddenCmdTablePtr = (Tcl_HashTable *) | ||
ckalloc((unsigned) sizeof(Tcl_HashTable)); | ||
Tcl_InitHashTable(hiddenCmdTablePtr, TCL_STRING_KEYS); | ||
iPtr->hiddenCmdTablePtr = hiddenCmdTablePtr; | ||
} | ||
/* | ||
* It is an error to move an exposed command to a hidden command with | ||
* hiddenCmdToken if a hidden command with the name hiddenCmdToken already | ||
* exists. | ||
*/ | ||
hPtr = Tcl_CreateHashEntry(hiddenCmdTablePtr, hiddenCmdToken, &new); | ||
if (!new) { | ||
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), | ||
"hidden command named \"", hiddenCmdToken, "\" already exists", | ||
(char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* Nb : This code is currently 'like' a rename to a specialy set apart | ||
* name table. Changes here and in TclRenameCommand must | ||
* be kept in synch untill the common parts are actually | ||
* factorized out. | ||
*/ | ||
/* | ||
* Remove the hash entry for the command from the interpreter command | ||
* table. This is like deleting the command, so bump its command epoch; | ||
* this invalidates any cached references that point to the command. | ||
*/ | ||
if (cmdPtr->hPtr != NULL) { | ||
Tcl_DeleteHashEntry(cmdPtr->hPtr); | ||
cmdPtr->hPtr = (Tcl_HashEntry *) NULL; | ||
cmdPtr->cmdEpoch++; | ||
} | ||
/* | ||
* Now link the hash table entry with the command structure. | ||
* We ensured above that the nsPtr was right. | ||
*/ | ||
cmdPtr->hPtr = hPtr; | ||
Tcl_SetHashValue(hPtr, (ClientData) cmdPtr); | ||
/* | ||
* If the command being hidden has a compile procedure, increment the | ||
* interpreter's compileEpoch to invalidate its compiled code. This | ||
* makes sure that we don't later try to execute old code compiled with | ||
* command-specific (i.e., inline) bytecodes for the now-hidden | ||
* command. This field is checked in Tcl_EvalObj and ObjInterpProc, | ||
* and code whose compilation epoch doesn't match is recompiled. | ||
*/ | ||
if (cmdPtr->compileProc != NULL) { | ||
iPtr->compileEpoch++; | ||
} | ||
return TCL_OK; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_ExposeCommand -- | ||
* | ||
* Makes a previously hidden command callable from inside the | ||
* interpreter instead of only by its ancestors. | ||
* | ||
* Results: | ||
* A standard Tcl result. If an error occurs, a message is left | ||
* in the interp's result. | ||
* | ||
* Side effects: | ||
* Moves commands from one hash table to another. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
int | ||
Tcl_ExposeCommand(interp, hiddenCmdToken, cmdName) | ||
Tcl_Interp *interp; /* Interpreter in which to make command | ||
* callable. */ | ||
char *hiddenCmdToken; /* Name of hidden command. */ | ||
char *cmdName; /* Name of to-be-exposed command. */ | ||
{ | ||
Interp *iPtr = (Interp *) interp; | ||
Command *cmdPtr; | ||
Namespace *nsPtr; | ||
Tcl_HashEntry *hPtr; | ||
Tcl_HashTable *hiddenCmdTablePtr; | ||
int new; | ||
if (iPtr->flags & DELETED) { | ||
/* | ||
* The interpreter is being deleted. Do not create any new | ||
* structures, because it is not safe to modify the interpreter. | ||
*/ | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* Check that we have a regular name for the command | ||
* (that the user is not trying to do an expose and a rename | ||
* (to another namespace) at the same time) | ||
*/ | ||
if (strstr(cmdName, "::") != NULL) { | ||
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), | ||
"can not expose to a namespace ", | ||
"(use expose to toplevel, then rename)", | ||
(char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* Get the command from the hidden command table: | ||
*/ | ||
hPtr = NULL; | ||
hiddenCmdTablePtr = iPtr->hiddenCmdTablePtr; | ||
if (hiddenCmdTablePtr != NULL) { | ||
hPtr = Tcl_FindHashEntry(hiddenCmdTablePtr, hiddenCmdToken); | ||
} | ||
if (hPtr == (Tcl_HashEntry *) NULL) { | ||
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), | ||
"unknown hidden command \"", hiddenCmdToken, | ||
"\"", (char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
cmdPtr = (Command *) Tcl_GetHashValue(hPtr); | ||
/* | ||
* Check that we have a true global namespace | ||
* command (enforced by Tcl_HideCommand() but let's double | ||
* check. (If it was not, we would not really know how to | ||
* handle it). | ||
*/ | ||
if ( cmdPtr->nsPtr != iPtr->globalNsPtr ) { | ||
/* | ||
* This case is theoritically impossible, | ||
* we might rather panic() than 'nicely' erroring out ? | ||
*/ | ||
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), | ||
"trying to expose a non global command name space command", | ||
(char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
/* This is the global table */ | ||
nsPtr = cmdPtr->nsPtr; | ||
/* | ||
* It is an error to overwrite an existing exposed command as a result | ||
* of exposing a previously hidden command. | ||
*/ | ||
hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, cmdName, &new); | ||
if (!new) { | ||
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), | ||
"exposed command \"", cmdName, | ||
"\" already exists", (char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* Remove the hash entry for the command from the interpreter hidden | ||
* command table. | ||
*/ | ||
if (cmdPtr->hPtr != NULL) { | ||
Tcl_DeleteHashEntry(cmdPtr->hPtr); | ||
cmdPtr->hPtr = NULL; | ||
} | ||
/* | ||
* Now link the hash table entry with the command structure. | ||
* This is like creating a new command, so deal with any shadowing | ||
* of commands in the global namespace. | ||
*/ | ||
cmdPtr->hPtr = hPtr; | ||
Tcl_SetHashValue(hPtr, (ClientData) cmdPtr); | ||
/* | ||
* Not needed as we are only in the global namespace | ||
* (but would be needed again if we supported namespace command hiding) | ||
* | ||
* TclResetShadowedCmdRefs(interp, cmdPtr); | ||
*/ | ||
/* | ||
* If the command being exposed has a compile procedure, increment | ||
* interpreter's compileEpoch to invalidate its compiled code. This | ||
* makes sure that we don't later try to execute old code compiled | ||
* assuming the command is hidden. This field is checked in Tcl_EvalObj | ||
* and ObjInterpProc, and code whose compilation epoch doesn't match is | ||
* recompiled. | ||
*/ | ||
if (cmdPtr->compileProc != NULL) { | ||
iPtr->compileEpoch++; | ||
} | ||
return TCL_OK; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_CreateCommand -- | ||
* | ||
* Define a new command in a command table. | ||
* | ||
* Results: | ||
* The return value is a token for the command, which can | ||
* be used in future calls to Tcl_GetCommandName. | ||
* | ||
* Side effects: | ||
* If a command named cmdName already exists for interp, it is deleted. | ||
* In the future, when cmdName is seen as the name of a command by | ||
* Tcl_Eval, proc will be called. To support the bytecode interpreter, | ||
* the command is created with a wrapper Tcl_ObjCmdProc | ||
* (TclInvokeStringCommand) that eventially calls proc. When the | ||
* command is deleted from the table, deleteProc will be called. | ||
* See the manual entry for details on the calling sequence. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
Tcl_Command | ||
Tcl_CreateCommand(interp, cmdName, proc, clientData, deleteProc) | ||
Tcl_Interp *interp; /* Token for command interpreter returned by | ||
* a previous call to Tcl_CreateInterp. */ | ||
char *cmdName; /* Name of command. If it contains namespace | ||
* qualifiers, the new command is put in the | ||
* specified namespace; otherwise it is put | ||
* in the global namespace. */ | ||
Tcl_CmdProc *proc; /* Procedure to associate with cmdName. */ | ||
ClientData clientData; /* Arbitrary value passed to string proc. */ | ||
Tcl_CmdDeleteProc *deleteProc; | ||
/* If not NULL, gives a procedure to call | ||
* when this command is deleted. */ | ||
{ | ||
Interp *iPtr = (Interp *) interp; | ||
ImportRef *oldRefPtr = NULL; | ||
Namespace *nsPtr, *dummy1, *dummy2; | ||
Command *cmdPtr, *refCmdPtr; | ||
Tcl_HashEntry *hPtr; | ||
char *tail; | ||
int new; | ||
ImportedCmdData *dataPtr; | ||
if (iPtr->flags & DELETED) { | ||
/* | ||
* The interpreter is being deleted. Don't create any new | ||
* commands; it's not safe to muck with the interpreter anymore. | ||
*/ | ||
return (Tcl_Command) NULL; | ||
} | ||
/* | ||
* Determine where the command should reside. If its name contains | ||
* namespace qualifiers, we put it in the specified namespace; | ||
* otherwise, we always put it in the global namespace. | ||
*/ | ||
if (strstr(cmdName, "::") != NULL) { | ||
TclGetNamespaceForQualName(interp, cmdName, (Namespace *) NULL, | ||
CREATE_NS_IF_UNKNOWN, &nsPtr, &dummy1, &dummy2, &tail); | ||
if ((nsPtr == NULL) || (tail == NULL)) { | ||
return (Tcl_Command) NULL; | ||
} | ||
} else { | ||
nsPtr = iPtr->globalNsPtr; | ||
tail = cmdName; | ||
} | ||
hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, tail, &new); | ||
if (!new) { | ||
/* | ||
* Command already exists. Delete the old one. | ||
* Be careful to preserve any existing import links so we can | ||
* restore them down below. That way, you can redefine a | ||
* command and its import status will remain intact. | ||
*/ | ||
cmdPtr = (Command *) Tcl_GetHashValue(hPtr); | ||
oldRefPtr = cmdPtr->importRefPtr; | ||
cmdPtr->importRefPtr = NULL; | ||
Tcl_DeleteCommandFromToken(interp, (Tcl_Command) cmdPtr); | ||
hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, tail, &new); | ||
if (!new) { | ||
/* | ||
* If the deletion callback recreated the command, just throw | ||
* away the new command (if we try to delete it again, we | ||
* could get stuck in an infinite loop). | ||
*/ | ||
ckfree((char*) Tcl_GetHashValue(hPtr)); | ||
} | ||
} | ||
cmdPtr = (Command *) ckalloc(sizeof(Command)); | ||
Tcl_SetHashValue(hPtr, cmdPtr); | ||
cmdPtr->hPtr = hPtr; | ||
cmdPtr->nsPtr = nsPtr; | ||
cmdPtr->refCount = 1; | ||
cmdPtr->cmdEpoch = 0; | ||
cmdPtr->compileProc = (CompileProc *) NULL; | ||
cmdPtr->objProc = TclInvokeStringCommand; | ||
cmdPtr->objClientData = (ClientData) cmdPtr; | ||
cmdPtr->proc = proc; | ||
cmdPtr->clientData = clientData; | ||
cmdPtr->deleteProc = deleteProc; | ||
cmdPtr->deleteData = clientData; | ||
cmdPtr->deleted = 0; | ||
cmdPtr->importRefPtr = NULL; | ||
/* | ||
* Plug in any existing import references found above. Be sure | ||
* to update all of these references to point to the new command. | ||
*/ | ||
if (oldRefPtr != NULL) { | ||
cmdPtr->importRefPtr = oldRefPtr; | ||
while (oldRefPtr != NULL) { | ||
refCmdPtr = oldRefPtr->importedCmdPtr; | ||
dataPtr = (ImportedCmdData*)refCmdPtr->objClientData; | ||
dataPtr->realCmdPtr = cmdPtr; | ||
oldRefPtr = oldRefPtr->nextPtr; | ||
} | ||
} | ||
/* | ||
* We just created a command, so in its namespace and all of its parent | ||
* namespaces, it may shadow global commands with the same name. If any | ||
* shadowed commands are found, invalidate all cached command references | ||
* in the affected namespaces. | ||
*/ | ||
TclResetShadowedCmdRefs(interp, cmdPtr); | ||
return (Tcl_Command) cmdPtr; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_CreateObjCommand -- | ||
* | ||
* Define a new object-based command in a command table. | ||
* | ||
* Results: | ||
* The return value is a token for the command, which can | ||
* be used in future calls to Tcl_GetCommandName. | ||
* | ||
* Side effects: | ||
* If no command named "cmdName" already exists for interp, one is | ||
* created. Otherwise, if a command does exist, then if the | ||
* object-based Tcl_ObjCmdProc is TclInvokeStringCommand, we assume | ||
* Tcl_CreateCommand was called previously for the same command and | ||
* just set its Tcl_ObjCmdProc to the argument "proc"; otherwise, we | ||
* delete the old command. | ||
* | ||
* In the future, during bytecode evaluation when "cmdName" is seen as | ||
* the name of a command by Tcl_EvalObj or Tcl_Eval, the object-based | ||
* Tcl_ObjCmdProc proc will be called. When the command is deleted from | ||
* the table, deleteProc will be called. See the manual entry for | ||
* details on the calling sequence. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
Tcl_Command | ||
Tcl_CreateObjCommand(interp, cmdName, proc, clientData, deleteProc) | ||
Tcl_Interp *interp; /* Token for command interpreter (returned | ||
* by previous call to Tcl_CreateInterp). */ | ||
char *cmdName; /* Name of command. If it contains namespace | ||
* qualifiers, the new command is put in the | ||
* specified namespace; otherwise it is put | ||
* in the global namespace. */ | ||
Tcl_ObjCmdProc *proc; /* Object-based procedure to associate with | ||
* name. */ | ||
ClientData clientData; /* Arbitrary value to pass to object | ||
* procedure. */ | ||
Tcl_CmdDeleteProc *deleteProc; | ||
/* If not NULL, gives a procedure to call | ||
* when this command is deleted. */ | ||
{ | ||
Interp *iPtr = (Interp *) interp; | ||
ImportRef *oldRefPtr = NULL; | ||
Namespace *nsPtr, *dummy1, *dummy2; | ||
Command *cmdPtr, *refCmdPtr; | ||
Tcl_HashEntry *hPtr; | ||
char *tail; | ||
int new; | ||
ImportedCmdData *dataPtr; | ||
if (iPtr->flags & DELETED) { | ||
/* | ||
* The interpreter is being deleted. Don't create any new | ||
* commands; it's not safe to muck with the interpreter anymore. | ||
*/ | ||
return (Tcl_Command) NULL; | ||
} | ||
/* | ||
* Determine where the command should reside. If its name contains | ||
* namespace qualifiers, we put it in the specified namespace; | ||
* otherwise, we always put it in the global namespace. | ||
*/ | ||
if (strstr(cmdName, "::") != NULL) { | ||
TclGetNamespaceForQualName(interp, cmdName, (Namespace *) NULL, | ||
CREATE_NS_IF_UNKNOWN, &nsPtr, &dummy1, &dummy2, &tail); | ||
if ((nsPtr == NULL) || (tail == NULL)) { | ||
return (Tcl_Command) NULL; | ||
} | ||
} else { | ||
nsPtr = iPtr->globalNsPtr; | ||
tail = cmdName; | ||
} | ||
hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, tail, &new); | ||
if (!new) { | ||
cmdPtr = (Command *) Tcl_GetHashValue(hPtr); | ||
/* | ||
* Command already exists. If its object-based Tcl_ObjCmdProc is | ||
* TclInvokeStringCommand, we just set its Tcl_ObjCmdProc to the | ||
* argument "proc". Otherwise, we delete the old command. | ||
*/ | ||
if (cmdPtr->objProc == TclInvokeStringCommand) { | ||
cmdPtr->objProc = proc; | ||
cmdPtr->objClientData = clientData; | ||
cmdPtr->deleteProc = deleteProc; | ||
cmdPtr->deleteData = clientData; | ||
return (Tcl_Command) cmdPtr; | ||
} | ||
/* | ||
* Otherwise, we delete the old command. Be careful to preserve | ||
* any existing import links so we can restore them down below. | ||
* That way, you can redefine a command and its import status | ||
* will remain intact. | ||
*/ | ||
oldRefPtr = cmdPtr->importRefPtr; | ||
cmdPtr->importRefPtr = NULL; | ||
Tcl_DeleteCommandFromToken(interp, (Tcl_Command) cmdPtr); | ||
hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, tail, &new); | ||
if (!new) { | ||
/* | ||
* If the deletion callback recreated the command, just throw | ||
* away the new command (if we try to delete it again, we | ||
* could get stuck in an infinite loop). | ||
*/ | ||
ckfree((char *) Tcl_GetHashValue(hPtr)); | ||
} | ||
} | ||
cmdPtr = (Command *) ckalloc(sizeof(Command)); | ||
Tcl_SetHashValue(hPtr, cmdPtr); | ||
cmdPtr->hPtr = hPtr; | ||
cmdPtr->nsPtr = nsPtr; | ||
cmdPtr->refCount = 1; | ||
cmdPtr->cmdEpoch = 0; | ||
cmdPtr->compileProc = (CompileProc *) NULL; | ||
cmdPtr->objProc = proc; | ||
cmdPtr->objClientData = clientData; | ||
cmdPtr->proc = TclInvokeObjectCommand; | ||
cmdPtr->clientData = (ClientData) cmdPtr; | ||
cmdPtr->deleteProc = deleteProc; | ||
cmdPtr->deleteData = clientData; | ||
cmdPtr->deleted = 0; | ||
cmdPtr->importRefPtr = NULL; | ||
/* | ||
* Plug in any existing import references found above. Be sure | ||
* to update all of these references to point to the new command. | ||
*/ | ||
if (oldRefPtr != NULL) { | ||
cmdPtr->importRefPtr = oldRefPtr; | ||
while (oldRefPtr != NULL) { | ||
refCmdPtr = oldRefPtr->importedCmdPtr; | ||
dataPtr = (ImportedCmdData*)refCmdPtr->objClientData; | ||
dataPtr->realCmdPtr = cmdPtr; | ||
oldRefPtr = oldRefPtr->nextPtr; | ||
} | ||
} | ||
/* | ||
* We just created a command, so in its namespace and all of its parent | ||
* namespaces, it may shadow global commands with the same name. If any | ||
* shadowed commands are found, invalidate all cached command references | ||
* in the affected namespaces. | ||
*/ | ||
TclResetShadowedCmdRefs(interp, cmdPtr); | ||
return (Tcl_Command) cmdPtr; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* TclInvokeStringCommand -- | ||
* | ||
* "Wrapper" Tcl_ObjCmdProc used to call an existing string-based | ||
* Tcl_CmdProc if no object-based procedure exists for a command. A | ||
* pointer to this procedure is stored as the Tcl_ObjCmdProc in a | ||
* Command structure. It simply turns around and calls the string | ||
* Tcl_CmdProc in the Command structure. | ||
* | ||
* Results: | ||
* A standard Tcl object result value. | ||
* | ||
* Side effects: | ||
* Besides those side effects of the called Tcl_CmdProc, | ||
* TclInvokeStringCommand allocates and frees storage. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
int | ||
TclInvokeStringCommand(clientData, interp, objc, objv) | ||
ClientData clientData; /* Points to command's Command structure. */ | ||
Tcl_Interp *interp; /* Current interpreter. */ | ||
register int objc; /* Number of arguments. */ | ||
Tcl_Obj *CONST objv[]; /* Argument objects. */ | ||
{ | ||
register Command *cmdPtr = (Command *) clientData; | ||
register int i; | ||
int result; | ||
/* | ||
* This procedure generates an argv array for the string arguments. It | ||
* starts out with stack-allocated space but uses dynamically-allocated | ||
* storage if needed. | ||
*/ | ||
#define NUM_ARGS 20 | ||
char *(argStorage[NUM_ARGS]); | ||
char **argv = argStorage; | ||
/* | ||
* Create the string argument array "argv". Make sure argv is large | ||
* enough to hold the objc arguments plus 1 extra for the zero | ||
* end-of-argv word. | ||
*/ | ||
if ((objc + 1) > NUM_ARGS) { | ||
argv = (char **) ckalloc((unsigned)(objc + 1) * sizeof(char *)); | ||
} | ||
for (i = 0; i < objc; i++) { | ||
argv[i] = Tcl_GetString(objv[i]); | ||
} | ||
argv[objc] = 0; | ||
/* | ||
* Invoke the command's string-based Tcl_CmdProc. | ||
*/ | ||
result = (*cmdPtr->proc)(cmdPtr->clientData, interp, objc, argv); | ||
/* | ||
* Free the argv array if malloc'ed storage was used. | ||
*/ | ||
if (argv != argStorage) { | ||
ckfree((char *) argv); | ||
} | ||
return result; | ||
#undef NUM_ARGS | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* TclInvokeObjectCommand -- | ||
* | ||
* "Wrapper" Tcl_CmdProc used to call an existing object-based | ||
* Tcl_ObjCmdProc if no string-based procedure exists for a command. | ||
* A pointer to this procedure is stored as the Tcl_CmdProc in a | ||
* Command structure. It simply turns around and calls the object | ||
* Tcl_ObjCmdProc in the Command structure. | ||
* | ||
* Results: | ||
* A standard Tcl string result value. | ||
* | ||
* Side effects: | ||
* Besides those side effects of the called Tcl_CmdProc, | ||
* TclInvokeStringCommand allocates and frees storage. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
int | ||
TclInvokeObjectCommand(clientData, interp, argc, argv) | ||
ClientData clientData; /* Points to command's Command structure. */ | ||
Tcl_Interp *interp; /* Current interpreter. */ | ||
int argc; /* Number of arguments. */ | ||
register char **argv; /* Argument strings. */ | ||
{ | ||
Command *cmdPtr = (Command *) clientData; | ||
register Tcl_Obj *objPtr; | ||
register int i; | ||
int length, result; | ||
/* | ||
* This procedure generates an objv array for object arguments that hold | ||
* the argv strings. It starts out with stack-allocated space but uses | ||
* dynamically-allocated storage if needed. | ||
*/ | ||
#define NUM_ARGS 20 | ||
Tcl_Obj *(argStorage[NUM_ARGS]); | ||
register Tcl_Obj **objv = argStorage; | ||
/* | ||
* Create the object argument array "objv". Make sure objv is large | ||
* enough to hold the objc arguments plus 1 extra for the zero | ||
* end-of-objv word. | ||
*/ | ||
if ((argc + 1) > NUM_ARGS) { | ||
objv = (Tcl_Obj **) | ||
ckalloc((unsigned)(argc + 1) * sizeof(Tcl_Obj *)); | ||
} | ||
for (i = 0; i < argc; i++) { | ||
length = strlen(argv[i]); | ||
TclNewObj(objPtr); | ||
TclInitStringRep(objPtr, argv[i], length); | ||
Tcl_IncrRefCount(objPtr); | ||
objv[i] = objPtr; | ||
} | ||
objv[argc] = 0; | ||
/* | ||
* Invoke the command's object-based Tcl_ObjCmdProc. | ||
*/ | ||
result = (*cmdPtr->objProc)(cmdPtr->objClientData, interp, argc, objv); | ||
/* | ||
* Move the interpreter's object result to the string result, | ||
* then reset the object result. | ||
*/ | ||
Tcl_SetResult(interp, TclGetString(Tcl_GetObjResult(interp)), | ||
TCL_VOLATILE); | ||
/* | ||
* Decrement the ref counts for the argument objects created above, | ||
* then free the objv array if malloc'ed storage was used. | ||
*/ | ||
for (i = 0; i < argc; i++) { | ||
objPtr = objv[i]; | ||
Tcl_DecrRefCount(objPtr); | ||
} | ||
if (objv != argStorage) { | ||
ckfree((char *) objv); | ||
} | ||
return result; | ||
#undef NUM_ARGS | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* TclRenameCommand -- | ||
* | ||
* Called to give an existing Tcl command a different name. Both the | ||
* old command name and the new command name can have "::" namespace | ||
* qualifiers. If the new command has a different namespace context, | ||
* the command will be moved to that namespace and will execute in | ||
* the context of that new namespace. | ||
* | ||
* If the new command name is NULL or the null string, the command is | ||
* deleted. | ||
* | ||
* Results: | ||
* Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. | ||
* | ||
* Side effects: | ||
* If anything goes wrong, an error message is returned in the | ||
* interpreter's result object. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
int | ||
TclRenameCommand(interp, oldName, newName) | ||
Tcl_Interp *interp; /* Current interpreter. */ | ||
char *oldName; /* Existing command name. */ | ||
char *newName; /* New command name. */ | ||
{ | ||
Interp *iPtr = (Interp *) interp; | ||
char *newTail; | ||
Namespace *cmdNsPtr, *newNsPtr, *dummy1, *dummy2; | ||
Tcl_Command cmd; | ||
Command *cmdPtr; | ||
Tcl_HashEntry *hPtr, *oldHPtr; | ||
int new, result; | ||
/* | ||
* Find the existing command. An error is returned if cmdName can't | ||
* be found. | ||
*/ | ||
cmd = Tcl_FindCommand(interp, oldName, (Tcl_Namespace *) NULL, | ||
/*flags*/ 0); | ||
cmdPtr = (Command *) cmd; | ||
if (cmdPtr == NULL) { | ||
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "can't ", | ||
((newName == NULL)||(*newName == '\0'))? "delete":"rename", | ||
" \"", oldName, "\": command doesn't exist", (char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
cmdNsPtr = cmdPtr->nsPtr; | ||
/* | ||
* If the new command name is NULL or empty, delete the command. Do this | ||
* with Tcl_DeleteCommandFromToken, since we already have the command. | ||
*/ | ||
if ((newName == NULL) || (*newName == '\0')) { | ||
Tcl_DeleteCommandFromToken(interp, cmd); | ||
return TCL_OK; | ||
} | ||
/* | ||
* Make sure that the destination command does not already exist. | ||
* The rename operation is like creating a command, so we should | ||
* automatically create the containing namespaces just like | ||
* Tcl_CreateCommand would. | ||
*/ | ||
TclGetNamespaceForQualName(interp, newName, (Namespace *) NULL, | ||
CREATE_NS_IF_UNKNOWN, &newNsPtr, &dummy1, &dummy2, &newTail); | ||
if ((newNsPtr == NULL) || (newTail == NULL)) { | ||
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), | ||
"can't rename to \"", newName, "\": bad command name", | ||
(char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
if (Tcl_FindHashEntry(&newNsPtr->cmdTable, newTail) != NULL) { | ||
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), | ||
"can't rename to \"", newName, | ||
"\": command already exists", (char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* Warning: any changes done in the code here are likely | ||
* to be needed in Tcl_HideCommand() code too. | ||
* (until the common parts are extracted out) --dl | ||
*/ | ||
/* | ||
* Put the command in the new namespace so we can check for an alias | ||
* loop. Since we are adding a new command to a namespace, we must | ||
* handle any shadowing of the global commands that this might create. | ||
*/ | ||
oldHPtr = cmdPtr->hPtr; | ||
hPtr = Tcl_CreateHashEntry(&newNsPtr->cmdTable, newTail, &new); | ||
Tcl_SetHashValue(hPtr, (ClientData) cmdPtr); | ||
cmdPtr->hPtr = hPtr; | ||
cmdPtr->nsPtr = newNsPtr; | ||
TclResetShadowedCmdRefs(interp, cmdPtr); | ||
/* | ||
* Now check for an alias loop. If we detect one, put everything back | ||
* the way it was and report the error. | ||
*/ | ||
result = TclPreventAliasLoop(interp, interp, (Tcl_Command) cmdPtr); | ||
if (result != TCL_OK) { | ||
Tcl_DeleteHashEntry(cmdPtr->hPtr); | ||
cmdPtr->hPtr = oldHPtr; | ||
cmdPtr->nsPtr = cmdNsPtr; | ||
return result; | ||
} | ||
/* | ||
* The new command name is okay, so remove the command from its | ||
* current namespace. This is like deleting the command, so bump | ||
* the cmdEpoch to invalidate any cached references to the command. | ||
*/ | ||
Tcl_DeleteHashEntry(oldHPtr); | ||
cmdPtr->cmdEpoch++; | ||
/* | ||
* If the command being renamed has a compile procedure, increment the | ||
* interpreter's compileEpoch to invalidate its compiled code. This | ||
* makes sure that we don't later try to execute old code compiled for | ||
* the now-renamed command. | ||
*/ | ||
if (cmdPtr->compileProc != NULL) { | ||
iPtr->compileEpoch++; | ||
} | ||
return TCL_OK; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_SetCommandInfo -- | ||
* | ||
* Modifies various information about a Tcl command. Note that | ||
* this procedure will not change a command's namespace; use | ||
* Tcl_RenameCommand to do that. Also, the isNativeObjectProc | ||
* member of *infoPtr is ignored. | ||
* | ||
* Results: | ||
* If cmdName exists in interp, then the information at *infoPtr | ||
* is stored with the command in place of the current information | ||
* and 1 is returned. If the command doesn't exist then 0 is | ||
* returned. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
int | ||
Tcl_SetCommandInfo(interp, cmdName, infoPtr) | ||
Tcl_Interp *interp; /* Interpreter in which to look | ||
* for command. */ | ||
char *cmdName; /* Name of desired command. */ | ||
Tcl_CmdInfo *infoPtr; /* Where to find information | ||
* to store in the command. */ | ||
{ | ||
Tcl_Command cmd; | ||
Command *cmdPtr; | ||
cmd = Tcl_FindCommand(interp, cmdName, (Tcl_Namespace *) NULL, | ||
/*flags*/ 0); | ||
if (cmd == (Tcl_Command) NULL) { | ||
return 0; | ||
} | ||
/* | ||
* The isNativeObjectProc and nsPtr members of *infoPtr are ignored. | ||
*/ | ||
cmdPtr = (Command *) cmd; | ||
cmdPtr->proc = infoPtr->proc; | ||
cmdPtr->clientData = infoPtr->clientData; | ||
if (infoPtr->objProc == (Tcl_ObjCmdProc *) NULL) { | ||
cmdPtr->objProc = TclInvokeStringCommand; | ||
cmdPtr->objClientData = (ClientData) cmdPtr; | ||
} else { | ||
cmdPtr->objProc = infoPtr->objProc; | ||
cmdPtr->objClientData = infoPtr->objClientData; | ||
} | ||
cmdPtr->deleteProc = infoPtr->deleteProc; | ||
cmdPtr->deleteData = infoPtr->deleteData; | ||
return 1; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_GetCommandInfo -- | ||
* | ||
* Returns various information about a Tcl command. | ||
* | ||
* Results: | ||
* If cmdName exists in interp, then *infoPtr is modified to | ||
* hold information about cmdName and 1 is returned. If the | ||
* command doesn't exist then 0 is returned and *infoPtr isn't | ||
* modified. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
int | ||
Tcl_GetCommandInfo(interp, cmdName, infoPtr) | ||
Tcl_Interp *interp; /* Interpreter in which to look | ||
* for command. */ | ||
char *cmdName; /* Name of desired command. */ | ||
Tcl_CmdInfo *infoPtr; /* Where to store information about | ||
* command. */ | ||
{ | ||
Tcl_Command cmd; | ||
Command *cmdPtr; | ||
cmd = Tcl_FindCommand(interp, cmdName, (Tcl_Namespace *) NULL, | ||
/*flags*/ 0); | ||
if (cmd == (Tcl_Command) NULL) { | ||
return 0; | ||
} | ||
/* | ||
* Set isNativeObjectProc 1 if objProc was registered by a call to | ||
* Tcl_CreateObjCommand. Otherwise set it to 0. | ||
*/ | ||
cmdPtr = (Command *) cmd; | ||
infoPtr->isNativeObjectProc = | ||
(cmdPtr->objProc != TclInvokeStringCommand); | ||
infoPtr->objProc = cmdPtr->objProc; | ||
infoPtr->objClientData = cmdPtr->objClientData; | ||
infoPtr->proc = cmdPtr->proc; | ||
infoPtr->clientData = cmdPtr->clientData; | ||
infoPtr->deleteProc = cmdPtr->deleteProc; | ||
infoPtr->deleteData = cmdPtr->deleteData; | ||
infoPtr->namespacePtr = (Tcl_Namespace *) cmdPtr->nsPtr; | ||
return 1; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_GetCommandName -- | ||
* | ||
* Given a token returned by Tcl_CreateCommand, this procedure | ||
* returns the current name of the command (which may have changed | ||
* due to renaming). | ||
* | ||
* Results: | ||
* The return value is the name of the given command. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
char * | ||
Tcl_GetCommandName(interp, command) | ||
Tcl_Interp *interp; /* Interpreter containing the command. */ | ||
Tcl_Command command; /* Token for command returned by a previous | ||
* call to Tcl_CreateCommand. The command | ||
* must not have been deleted. */ | ||
{ | ||
Command *cmdPtr = (Command *) command; | ||
if ((cmdPtr == NULL) || (cmdPtr->hPtr == NULL)) { | ||
/* | ||
* This should only happen if command was "created" after the | ||
* interpreter began to be deleted, so there isn't really any | ||
* command. Just return an empty string. | ||
*/ | ||
return ""; | ||
} | ||
return Tcl_GetHashKey(cmdPtr->hPtr->tablePtr, cmdPtr->hPtr); | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_GetCommandFullName -- | ||
* | ||
* Given a token returned by, e.g., Tcl_CreateCommand or | ||
* Tcl_FindCommand, this procedure appends to an object the command's | ||
* full name, qualified by a sequence of parent namespace names. The | ||
* command's fully-qualified name may have changed due to renaming. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* The command's fully-qualified name is appended to the string | ||
* representation of objPtr. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
void | ||
Tcl_GetCommandFullName(interp, command, objPtr) | ||
Tcl_Interp *interp; /* Interpreter containing the command. */ | ||
Tcl_Command command; /* Token for command returned by a previous | ||
* call to Tcl_CreateCommand. The command | ||
* must not have been deleted. */ | ||
Tcl_Obj *objPtr; /* Points to the object onto which the | ||
* command's full name is appended. */ | ||
{ | ||
Interp *iPtr = (Interp *) interp; | ||
register Command *cmdPtr = (Command *) command; | ||
char *name; | ||
/* | ||
* Add the full name of the containing namespace, followed by the "::" | ||
* separator, and the command name. | ||
*/ | ||
if (cmdPtr != NULL) { | ||
if (cmdPtr->nsPtr != NULL) { | ||
Tcl_AppendToObj(objPtr, cmdPtr->nsPtr->fullName, -1); | ||
if (cmdPtr->nsPtr != iPtr->globalNsPtr) { | ||
Tcl_AppendToObj(objPtr, "::", 2); | ||
} | ||
} | ||
if (cmdPtr->hPtr != NULL) { | ||
name = Tcl_GetHashKey(cmdPtr->hPtr->tablePtr, cmdPtr->hPtr); | ||
Tcl_AppendToObj(objPtr, name, -1); | ||
} | ||
} | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_DeleteCommand -- | ||
* | ||
* Remove the given command from the given interpreter. | ||
* | ||
* Results: | ||
* 0 is returned if the command was deleted successfully. | ||
* -1 is returned if there didn't exist a command by that name. | ||
* | ||
* Side effects: | ||
* cmdName will no longer be recognized as a valid command for | ||
* interp. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
int | ||
Tcl_DeleteCommand(interp, cmdName) | ||
Tcl_Interp *interp; /* Token for command interpreter (returned | ||
* by a previous Tcl_CreateInterp call). */ | ||
char *cmdName; /* Name of command to remove. */ | ||
{ | ||
Tcl_Command cmd; | ||
/* | ||
* Find the desired command and delete it. | ||
*/ | ||
cmd = Tcl_FindCommand(interp, cmdName, (Tcl_Namespace *) NULL, | ||
/*flags*/ 0); | ||
if (cmd == (Tcl_Command) NULL) { | ||
return -1; | ||
} | ||
return Tcl_DeleteCommandFromToken(interp, cmd); | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_DeleteCommandFromToken -- | ||
* | ||
* Removes the given command from the given interpreter. This procedure | ||
* resembles Tcl_DeleteCommand, but takes a Tcl_Command token instead | ||
* of a command name for efficiency. | ||
* | ||
* Results: | ||
* 0 is returned if the command was deleted successfully. | ||
* -1 is returned if there didn't exist a command by that name. | ||
* | ||
* Side effects: | ||
* The command specified by "cmd" will no longer be recognized as a | ||
* valid command for "interp". | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
int | ||
Tcl_DeleteCommandFromToken(interp, cmd) | ||
Tcl_Interp *interp; /* Token for command interpreter returned by | ||
* a previous call to Tcl_CreateInterp. */ | ||
Tcl_Command cmd; /* Token for command to delete. */ | ||
{ | ||
Interp *iPtr = (Interp *) interp; | ||
Command *cmdPtr = (Command *) cmd; | ||
ImportRef *refPtr, *nextRefPtr; | ||
Tcl_Command importCmd; | ||
/* | ||
* The code here is tricky. We can't delete the hash table entry | ||
* before invoking the deletion callback because there are cases | ||
* where the deletion callback needs to invoke the command (e.g. | ||
* object systems such as OTcl). However, this means that the | ||
* callback could try to delete or rename the command. The deleted | ||
* flag allows us to detect these cases and skip nested deletes. | ||
*/ | ||
if (cmdPtr->deleted) { | ||
/* | ||
* Another deletion is already in progress. Remove the hash | ||
* table entry now, but don't invoke a callback or free the | ||
* command structure. | ||
*/ | ||
Tcl_DeleteHashEntry(cmdPtr->hPtr); | ||
cmdPtr->hPtr = NULL; | ||
return 0; | ||
} | ||
/* | ||
* If the command being deleted has a compile procedure, increment the | ||
* interpreter's compileEpoch to invalidate its compiled code. This | ||
* makes sure that we don't later try to execute old code compiled with | ||
* command-specific (i.e., inline) bytecodes for the now-deleted | ||
* command. This field is checked in Tcl_EvalObj and ObjInterpProc, and | ||
* code whose compilation epoch doesn't match is recompiled. | ||
*/ | ||
if (cmdPtr->compileProc != NULL) { | ||
iPtr->compileEpoch++; | ||
} | ||
cmdPtr->deleted = 1; | ||
if (cmdPtr->deleteProc != NULL) { | ||
/* | ||
* Delete the command's client data. If this was an imported command | ||
* created when a command was imported into a namespace, this client | ||
* data will be a pointer to a ImportedCmdData structure describing | ||
* the "real" command that this imported command refers to. | ||
*/ | ||
/* | ||
* If you are getting a crash during the call to deleteProc and | ||
* cmdPtr->deleteProc is a pointer to the function free(), the | ||
* most likely cause is that your extension allocated memory | ||
* for the clientData argument to Tcl_CreateObjCommand() with | ||
* the ckalloc() macro and you are now trying to deallocate | ||
* this memory with free() instead of ckfree(). You should | ||
* pass a pointer to your own method that calls ckfree(). | ||
*/ | ||
(*cmdPtr->deleteProc)(cmdPtr->deleteData); | ||
} | ||
/* | ||
* Bump the command epoch counter. This will invalidate all cached | ||
* references that point to this command. | ||
*/ | ||
cmdPtr->cmdEpoch++; | ||
/* | ||
* If this command was imported into other namespaces, then imported | ||
* commands were created that refer back to this command. Delete these | ||
* imported commands now. | ||
*/ | ||
for (refPtr = cmdPtr->importRefPtr; refPtr != NULL; | ||
refPtr = nextRefPtr) { | ||
nextRefPtr = refPtr->nextPtr; | ||
importCmd = (Tcl_Command) refPtr->importedCmdPtr; | ||
Tcl_DeleteCommandFromToken(interp, importCmd); | ||
} | ||
/* | ||
* Don't use hPtr to delete the hash entry here, because it's | ||
* possible that the deletion callback renamed the command. | ||
* Instead, use cmdPtr->hptr, and make sure that no-one else | ||
* has already deleted the hash entry. | ||
*/ | ||
if (cmdPtr->hPtr != NULL) { | ||
Tcl_DeleteHashEntry(cmdPtr->hPtr); | ||
} | ||
/* | ||
* Mark the Command structure as no longer valid. This allows | ||
* TclExecuteByteCode to recognize when a Command has logically been | ||
* deleted and a pointer to this Command structure cached in a CmdName | ||
* object is invalid. TclExecuteByteCode will look up the command again | ||
* in the interpreter's command hashtable. | ||
*/ | ||
cmdPtr->objProc = NULL; | ||
/* | ||
* Now free the Command structure, unless there is another reference to | ||
* it from a CmdName Tcl object in some ByteCode code sequence. In that | ||
* case, delay the cleanup until all references are either discarded | ||
* (when a ByteCode is freed) or replaced by a new reference (when a | ||
* cached CmdName Command reference is found to be invalid and | ||
* TclExecuteByteCode looks up the command in the command hashtable). | ||
*/ | ||
TclCleanupCommand(cmdPtr); | ||
return 0; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* TclCleanupCommand -- | ||
* | ||
* This procedure frees up a Command structure unless it is still | ||
* referenced from an interpreter's command hashtable or from a CmdName | ||
* Tcl object representing the name of a command in a ByteCode | ||
* instruction sequence. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* Memory gets freed unless a reference to the Command structure still | ||
* exists. In that case the cleanup is delayed until the command is | ||
* deleted or when the last ByteCode referring to it is freed. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
void | ||
TclCleanupCommand(cmdPtr) | ||
register Command *cmdPtr; /* Points to the Command structure to | ||
* be freed. */ | ||
{ | ||
cmdPtr->refCount--; | ||
if (cmdPtr->refCount <= 0) { | ||
ckfree((char *) cmdPtr); | ||
} | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_CreateMathFunc -- | ||
* | ||
* Creates a new math function for expressions in a given | ||
* interpreter. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* The function defined by "name" is created or redefined. If the | ||
* function already exists then its definition is replaced; this | ||
* includes the builtin functions. Redefining a builtin function forces | ||
* all existing code to be invalidated since that code may be compiled | ||
* using an instruction specific to the replaced function. In addition, | ||
* redefioning a non-builtin function will force existing code to be | ||
* invalidated if the number of arguments has changed. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
void | ||
Tcl_CreateMathFunc(interp, name, numArgs, argTypes, proc, clientData) | ||
Tcl_Interp *interp; /* Interpreter in which function is | ||
* to be available. */ | ||
char *name; /* Name of function (e.g. "sin"). */ | ||
int numArgs; /* Nnumber of arguments required by | ||
* function. */ | ||
Tcl_ValueType *argTypes; /* Array of types acceptable for | ||
* each argument. */ | ||
Tcl_MathProc *proc; /* Procedure that implements the | ||
* math function. */ | ||
ClientData clientData; /* Additional value to pass to the | ||
* function. */ | ||
{ | ||
Interp *iPtr = (Interp *) interp; | ||
Tcl_HashEntry *hPtr; | ||
MathFunc *mathFuncPtr; | ||
int new, i; | ||
hPtr = Tcl_CreateHashEntry(&iPtr->mathFuncTable, name, &new); | ||
if (new) { | ||
Tcl_SetHashValue(hPtr, ckalloc(sizeof(MathFunc))); | ||
} | ||
mathFuncPtr = (MathFunc *) Tcl_GetHashValue(hPtr); | ||
if (!new) { | ||
if (mathFuncPtr->builtinFuncIndex >= 0) { | ||
/* | ||
* We are redefining a builtin math function. Invalidate the | ||
* interpreter's existing code by incrementing its | ||
* compileEpoch member. This field is checked in Tcl_EvalObj | ||
* and ObjInterpProc, and code whose compilation epoch doesn't | ||
* match is recompiled. Newly compiled code will no longer | ||
* treat the function as builtin. | ||
*/ | ||
iPtr->compileEpoch++; | ||
} else { | ||
/* | ||
* A non-builtin function is being redefined. We must invalidate | ||
* existing code if the number of arguments has changed. This | ||
* is because existing code was compiled assuming that number. | ||
*/ | ||
if (numArgs != mathFuncPtr->numArgs) { | ||
iPtr->compileEpoch++; | ||
} | ||
} | ||
} | ||
mathFuncPtr->builtinFuncIndex = -1; /* can't be a builtin function */ | ||
if (numArgs > MAX_MATH_ARGS) { | ||
numArgs = MAX_MATH_ARGS; | ||
} | ||
mathFuncPtr->numArgs = numArgs; | ||
for (i = 0; i < numArgs; i++) { | ||
mathFuncPtr->argTypes[i] = argTypes[i]; | ||
} | ||
mathFuncPtr->proc = proc; | ||
mathFuncPtr->clientData = clientData; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_EvalObjEx -- | ||
* | ||
* Execute Tcl commands stored in a Tcl object. These commands are | ||
* compiled into bytecodes if necessary, unless TCL_EVAL_DIRECT | ||
* is specified. | ||
* | ||
* Results: | ||
* The return value is one of the return codes defined in tcl.h | ||
* (such as TCL_OK), and the interpreter's result contains a value | ||
* to supplement the return code. | ||
* | ||
* Side effects: | ||
* The object is converted, if necessary, to a ByteCode object that | ||
* holds the bytecode instructions for the commands. Executing the | ||
* commands will almost certainly have side effects that depend | ||
* on those commands. | ||
* | ||
* Just as in Tcl_Eval, interp->termOffset is set to the offset of the | ||
* last character executed in the objPtr's string. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
int | ||
Tcl_EvalObjEx(interp, objPtr, flags) | ||
Tcl_Interp *interp; /* Token for command interpreter | ||
* (returned by a previous call to | ||
* Tcl_CreateInterp). */ | ||
register Tcl_Obj *objPtr; /* Pointer to object containing | ||
* commands to execute. */ | ||
int flags; /* Collection of OR-ed bits that | ||
* control the evaluation of the | ||
* script. Supported values are | ||
* TCL_EVAL_GLOBAL and | ||
* TCL_EVAL_DIRECT. */ | ||
{ | ||
register Interp *iPtr = (Interp *) interp; | ||
int evalFlags; /* Interp->evalFlags value when the | ||
* procedure was called. */ | ||
register ByteCode* codePtr; /* Tcl Internal type of bytecode. */ | ||
int oldCount = iPtr->cmdCount; /* Used to tell whether any commands | ||
* at all were executed. */ | ||
int numSrcBytes; | ||
int result; | ||
CallFrame *savedVarFramePtr; /* Saves old copy of iPtr->varFramePtr | ||
* in case TCL_EVAL_GLOBAL was set. */ | ||
Namespace *namespacePtr; | ||
Tcl_IncrRefCount(objPtr); | ||
if ((iPtr->flags & USE_EVAL_DIRECT) || (flags & TCL_EVAL_DIRECT)) { | ||
/* | ||
* We're not supposed to use the compiler or byte-code interpreter. | ||
* Let Tcl_EvalEx evaluate the command directly (and probably | ||
* more slowly). | ||
* | ||
* Pure List Optimization (no string representation). In this | ||
* case, we can safely use Tcl_EvalObjv instead and get an | ||
* appreciable improvement in execution speed. This is because it | ||
* allows us to avoid a setFromAny step that would just pack | ||
* everything into a string and back out again. | ||
* | ||
* USE_EVAL_DIRECT is a special flag used for testing purpose only | ||
* (ensure we go into the TCL_EVAL_DIRECT path, avoiding opt) | ||
*/ | ||
if (!(iPtr->flags & USE_EVAL_DIRECT) && | ||
(objPtr->typePtr == &tclListType) && /* is a list... */ | ||
(objPtr->bytes == NULL) /* ...without a string rep */) { | ||
register List *listRepPtr = | ||
(List *) objPtr->internalRep.otherValuePtr; | ||
result = Tcl_EvalObjv(interp, listRepPtr->elemCount, | ||
listRepPtr->elements, flags); | ||
} else { | ||
register char *p; | ||
p = Tcl_GetStringFromObj(objPtr, &numSrcBytes); | ||
result = Tcl_EvalEx(interp, p, numSrcBytes, flags); | ||
} | ||
Tcl_DecrRefCount(objPtr); | ||
return result; | ||
} | ||
/* | ||
* Prevent the object from being deleted as a side effect of evaling it. | ||
*/ | ||
savedVarFramePtr = iPtr->varFramePtr; | ||
if (flags & TCL_EVAL_GLOBAL) { | ||
iPtr->varFramePtr = NULL; | ||
} | ||
/* | ||
* Reset both the interpreter's string and object results and clear out | ||
* any error information. This makes sure that we return an empty | ||
* result if there are no commands in the command string. | ||
*/ | ||
Tcl_ResetResult(interp); | ||
/* | ||
* Check depth of nested calls to Tcl_Eval: if this gets too large, | ||
* it's probably because of an infinite loop somewhere. | ||
*/ | ||
iPtr->numLevels++; | ||
if (iPtr->numLevels > iPtr->maxNestingDepth) { | ||
Tcl_AppendToObj(Tcl_GetObjResult(interp), | ||
"too many nested calls to Tcl_EvalObj (infinite loop?)", -1); | ||
result = TCL_ERROR; | ||
goto done; | ||
} | ||
/* | ||
* On the Mac, we will never reach the default recursion limit before | ||
* blowing the stack. So we need to do a check here. | ||
*/ | ||
if (TclpCheckStackSpace() == 0) { | ||
/*NOTREACHED*/ | ||
Tcl_AppendToObj(Tcl_GetObjResult(interp), | ||
"too many nested calls to Tcl_EvalObj (infinite loop?)", -1); | ||
result = TCL_ERROR; | ||
goto done; | ||
} | ||
/* | ||
* If the interpreter has been deleted, return an error. | ||
*/ | ||
if (iPtr->flags & DELETED) { | ||
Tcl_ResetResult(interp); | ||
Tcl_AppendToObj(Tcl_GetObjResult(interp), | ||
"attempt to call eval in deleted interpreter", -1); | ||
Tcl_SetErrorCode(interp, "CORE", "IDELETE", | ||
"attempt to call eval in deleted interpreter", | ||
(char *) NULL); | ||
result = TCL_ERROR; | ||
goto done; | ||
} | ||
/* | ||
* Get the ByteCode from the object. If it exists, make sure it hasn't | ||
* been invalidated by, e.g., someone redefining a command with a | ||
* compile procedure (this might make the compiled code wrong). If | ||
* necessary, convert the object to be a ByteCode object and compile it. | ||
* Also, if the code was compiled in/for a different interpreter, | ||
* or for a different namespace, or for the same namespace but | ||
* with different name resolution rules, we recompile it. | ||
* | ||
* Precompiled objects, however, are immutable and therefore | ||
* they are not recompiled, even if the epoch has changed. | ||
* | ||
* To be pedantically correct, we should also check that the | ||
* originating procPtr is the same as the current context procPtr | ||
* (assuming one exists at all - none for global level). This | ||
* code is #def'ed out because [info body] was changed to never | ||
* return a bytecode type object, which should obviate us from | ||
* the extra checks here. | ||
*/ | ||
if (iPtr->varFramePtr != NULL) { | ||
namespacePtr = iPtr->varFramePtr->nsPtr; | ||
} else { | ||
namespacePtr = iPtr->globalNsPtr; | ||
} | ||
if (objPtr->typePtr == &tclByteCodeType) { | ||
codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr; | ||
if (((Interp *) *codePtr->interpHandle != iPtr) | ||
|| (codePtr->compileEpoch != iPtr->compileEpoch) | ||
#ifdef CHECK_PROC_ORIGINATION /* [Bug: 3412 Pedantic] */ | ||
|| (codePtr->procPtr != NULL && !(iPtr->varFramePtr && | ||
iPtr->varFramePtr->procPtr == codePtr->procPtr)) | ||
#endif | ||
|| (codePtr->nsPtr != namespacePtr) | ||
|| (codePtr->nsEpoch != namespacePtr->resolverEpoch)) { | ||
if (codePtr->flags & TCL_BYTECODE_PRECOMPILED) { | ||
if ((Interp *) *codePtr->interpHandle != iPtr) { | ||
panic("Tcl_EvalObj: compiled script jumped interps"); | ||
} | ||
codePtr->compileEpoch = iPtr->compileEpoch; | ||
} else { | ||
tclByteCodeType.freeIntRepProc(objPtr); | ||
} | ||
} | ||
} | ||
if (objPtr->typePtr != &tclByteCodeType) { | ||
iPtr->errorLine = 1; | ||
result = tclByteCodeType.setFromAnyProc(interp, objPtr); | ||
if (result != TCL_OK) { | ||
goto done; | ||
} | ||
} else { | ||
codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr; | ||
if (((Interp *) *codePtr->interpHandle != iPtr) | ||
|| (codePtr->compileEpoch != iPtr->compileEpoch)) { | ||
(*tclByteCodeType.freeIntRepProc)(objPtr); | ||
iPtr->errorLine = 1; | ||
result = (*tclByteCodeType.setFromAnyProc)(interp, objPtr); | ||
if (result != TCL_OK) { | ||
iPtr->numLevels--; | ||
return result; | ||
} | ||
} | ||
} | ||
codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr; | ||
/* | ||
* Extract then reset the compilation flags in the interpreter. | ||
* Resetting the flags must be done after any compilation. | ||
*/ | ||
evalFlags = iPtr->evalFlags; | ||
iPtr->evalFlags = 0; | ||
/* | ||
* Execute the commands. If the code was compiled from an empty string, | ||
* don't bother executing the code. | ||
*/ | ||
numSrcBytes = codePtr->numSrcBytes; | ||
if ((numSrcBytes > 0) || (codePtr->flags & TCL_BYTECODE_PRECOMPILED)) { | ||
/* | ||
* Increment the code's ref count while it is being executed. If | ||
* afterwards no references to it remain, free the code. | ||
*/ | ||
codePtr->refCount++; | ||
result = TclExecuteByteCode(interp, codePtr); | ||
codePtr->refCount--; | ||
if (codePtr->refCount <= 0) { | ||
TclCleanupByteCode(codePtr); | ||
} | ||
} else { | ||
result = TCL_OK; | ||
} | ||
/* | ||
* If no commands at all were executed, check for asynchronous | ||
* handlers so that they at least get one change to execute. | ||
* This is needed to handle event loops written in Tcl with | ||
* empty bodies. | ||
*/ | ||
if ((oldCount == iPtr->cmdCount) && Tcl_AsyncReady()) { | ||
result = Tcl_AsyncInvoke(interp, result); | ||
} | ||
/* | ||
* Update the interpreter's evaluation level count. If we are again at | ||
* the top level, process any unusual return code returned by the | ||
* evaluated code. | ||
*/ | ||
if (iPtr->numLevels == 1) { | ||
if (result == TCL_RETURN) { | ||
result = TclUpdateReturnInfo(iPtr); | ||
} | ||
if ((result != TCL_OK) && (result != TCL_ERROR) | ||
&& ((evalFlags & TCL_ALLOW_EXCEPTIONS) == 0)) { | ||
ProcessUnexpectedResult(interp, result); | ||
result = TCL_ERROR; | ||
} | ||
} | ||
/* | ||
* If an error occurred, record information about what was being | ||
* executed when the error occurred. | ||
*/ | ||
if ((result == TCL_ERROR) && !(iPtr->flags & ERR_ALREADY_LOGGED)) { | ||
RecordTracebackInfo(interp, objPtr, numSrcBytes); | ||
} | ||
/* | ||
* Set the interpreter's termOffset member to the offset of the | ||
* character just after the last one executed. We approximate the offset | ||
* of the last character executed by using the number of characters | ||
* compiled. | ||
*/ | ||
iPtr->termOffset = numSrcBytes; | ||
iPtr->flags &= ~ERR_ALREADY_LOGGED; | ||
done: | ||
TclDecrRefCount(objPtr); | ||
iPtr->varFramePtr = savedVarFramePtr; | ||
iPtr->numLevels--; | ||
return result; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* ProcessUnexpectedResult -- | ||
* | ||
* Procedure called by Tcl_EvalObj to set the interpreter's result | ||
* value to an appropriate error message when the code it evaluates | ||
* returns an unexpected result code (not TCL_OK and not TCL_ERROR) to | ||
* the topmost evaluation level. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* The interpreter result is set to an error message appropriate to | ||
* the result code. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static void | ||
ProcessUnexpectedResult(interp, returnCode) | ||
Tcl_Interp *interp; /* The interpreter in which the unexpected | ||
* result code was returned. */ | ||
int returnCode; /* The unexpected result code. */ | ||
{ | ||
Tcl_ResetResult(interp); | ||
if (returnCode == TCL_BREAK) { | ||
Tcl_AppendToObj(Tcl_GetObjResult(interp), | ||
"invoked \"break\" outside of a loop", -1); | ||
} else if (returnCode == TCL_CONTINUE) { | ||
Tcl_AppendToObj(Tcl_GetObjResult(interp), | ||
"invoked \"continue\" outside of a loop", -1); | ||
} else { | ||
char buf[30 + TCL_INTEGER_SPACE]; | ||
sprintf(buf, "command returned bad code: %d", returnCode); | ||
Tcl_SetResult(interp, buf, TCL_VOLATILE); | ||
} | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* RecordTracebackInfo -- | ||
* | ||
* Procedure called by Tcl_EvalObj to record information about what was | ||
* being executed when the error occurred. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* Appends information about the script being evaluated to the | ||
* interpreter's "errorInfo" variable. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static void | ||
RecordTracebackInfo(interp, objPtr, numSrcBytes) | ||
Tcl_Interp *interp; /* The interpreter in which the error | ||
* occurred. */ | ||
Tcl_Obj *objPtr; /* Points to object containing script whose | ||
* evaluation resulted in an error. */ | ||
int numSrcBytes; /* Number of bytes compiled in script. */ | ||
{ | ||
Interp *iPtr = (Interp *) interp; | ||
char buf[200]; | ||
char *ellipsis, *bytes; | ||
int length; | ||
/* | ||
* Decide how much of the command to print in the error message | ||
* (up to a certain number of bytes). | ||
*/ | ||
bytes = Tcl_GetStringFromObj(objPtr, &length); | ||
length = TclMin(numSrcBytes, length); | ||
ellipsis = ""; | ||
if (length > 150) { | ||
length = 150; | ||
ellipsis = " ..."; | ||
} | ||
if (!(iPtr->flags & ERR_IN_PROGRESS)) { | ||
sprintf(buf, "\n while executing\n\"%.*s%s\"", | ||
length, bytes, ellipsis); | ||
} else { | ||
sprintf(buf, "\n invoked from within\n\"%.*s%s\"", | ||
length, bytes, ellipsis); | ||
} | ||
Tcl_AddObjErrorInfo(interp, buf, -1); | ||
} | ||
/* | ||
*--------------------------------------------------------------------------- | ||
* | ||
* Tcl_ExprLong, Tcl_ExprDouble, Tcl_ExprBoolean -- | ||
* | ||
* Procedures to evaluate an expression and return its value in a | ||
* particular form. | ||
* | ||
* Results: | ||
* Each of the procedures below returns a standard Tcl result. If an | ||
* error occurs then an error message is left in the interp's result. | ||
* Otherwise the value of the expression, in the appropriate form, | ||
* is stored at *ptr. If the expression had a result that was | ||
* incompatible with the desired form then an error is returned. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*--------------------------------------------------------------------------- | ||
*/ | ||
int | ||
Tcl_ExprLong(interp, string, ptr) | ||
Tcl_Interp *interp; /* Context in which to evaluate the | ||
* expression. */ | ||
char *string; /* Expression to evaluate. */ | ||
long *ptr; /* Where to store result. */ | ||
{ | ||
register Tcl_Obj *exprPtr; | ||
Tcl_Obj *resultPtr; | ||
int length = strlen(string); | ||
int result = TCL_OK; | ||
if (length > 0) { | ||
exprPtr = Tcl_NewStringObj(string, length); | ||
Tcl_IncrRefCount(exprPtr); | ||
result = Tcl_ExprObj(interp, exprPtr, &resultPtr); | ||
if (result == TCL_OK) { | ||
/* | ||
* Store an integer based on the expression result. | ||
*/ | ||
if (resultPtr->typePtr == &tclIntType) { | ||
*ptr = resultPtr->internalRep.longValue; | ||
} else if (resultPtr->typePtr == &tclDoubleType) { | ||
*ptr = (long) resultPtr->internalRep.doubleValue; | ||
} else { | ||
Tcl_SetResult(interp, | ||
"expression didn't have numeric value", TCL_STATIC); | ||
result = TCL_ERROR; | ||
} | ||
Tcl_DecrRefCount(resultPtr); /* discard the result object */ | ||
} else { | ||
/* | ||
* Move the interpreter's object result to the string result, | ||
* then reset the object result. | ||
*/ | ||
Tcl_SetResult(interp, TclGetString(Tcl_GetObjResult(interp)), | ||
TCL_VOLATILE); | ||
} | ||
Tcl_DecrRefCount(exprPtr); /* discard the expression object */ | ||
} else { | ||
/* | ||
* An empty string. Just set the result integer to 0. | ||
*/ | ||
*ptr = 0; | ||
} | ||
return result; | ||
} | ||
int | ||
Tcl_ExprDouble(interp, string, ptr) | ||
Tcl_Interp *interp; /* Context in which to evaluate the | ||
* expression. */ | ||
char *string; /* Expression to evaluate. */ | ||
double *ptr; /* Where to store result. */ | ||
{ | ||
register Tcl_Obj *exprPtr; | ||
Tcl_Obj *resultPtr; | ||
int length = strlen(string); | ||
int result = TCL_OK; | ||
if (length > 0) { | ||
exprPtr = Tcl_NewStringObj(string, length); | ||
Tcl_IncrRefCount(exprPtr); | ||
result = Tcl_ExprObj(interp, exprPtr, &resultPtr); | ||
if (result == TCL_OK) { | ||
/* | ||
* Store a double based on the expression result. | ||
*/ | ||
if (resultPtr->typePtr == &tclIntType) { | ||
*ptr = (double) resultPtr->internalRep.longValue; | ||
} else if (resultPtr->typePtr == &tclDoubleType) { | ||
*ptr = resultPtr->internalRep.doubleValue; | ||
} else { | ||
Tcl_SetResult(interp, | ||
"expression didn't have numeric value", TCL_STATIC); | ||
result = TCL_ERROR; | ||
} | ||
Tcl_DecrRefCount(resultPtr); /* discard the result object */ | ||
} else { | ||
/* | ||
* Move the interpreter's object result to the string result, | ||
* then reset the object result. | ||
*/ | ||
Tcl_SetResult(interp, TclGetString(Tcl_GetObjResult(interp)), | ||
TCL_VOLATILE); | ||
} | ||
Tcl_DecrRefCount(exprPtr); /* discard the expression object */ | ||
} else { | ||
/* | ||
* An empty string. Just set the result double to 0.0. | ||
*/ | ||
*ptr = 0.0; | ||
} | ||
return result; | ||
} | ||
int | ||
Tcl_ExprBoolean(interp, string, ptr) | ||
Tcl_Interp *interp; /* Context in which to evaluate the | ||
* expression. */ | ||
char *string; /* Expression to evaluate. */ | ||
int *ptr; /* Where to store 0/1 result. */ | ||
{ | ||
register Tcl_Obj *exprPtr; | ||
Tcl_Obj *resultPtr; | ||
int length = strlen(string); | ||
int result = TCL_OK; | ||
if (length > 0) { | ||
exprPtr = Tcl_NewStringObj(string, length); | ||
Tcl_IncrRefCount(exprPtr); | ||
result = Tcl_ExprObj(interp, exprPtr, &resultPtr); | ||
if (result == TCL_OK) { | ||
/* | ||
* Store a boolean based on the expression result. | ||
*/ | ||
if (resultPtr->typePtr == &tclIntType) { | ||
*ptr = (resultPtr->internalRep.longValue != 0); | ||
} else if (resultPtr->typePtr == &tclDoubleType) { | ||
*ptr = (resultPtr->internalRep.doubleValue != 0.0); | ||
} else { | ||
result = Tcl_GetBooleanFromObj(interp, resultPtr, ptr); | ||
} | ||
Tcl_DecrRefCount(resultPtr); /* discard the result object */ | ||
} | ||
if (result != TCL_OK) { | ||
/* | ||
* Move the interpreter's object result to the string result, | ||
* then reset the object result. | ||
*/ | ||
Tcl_SetResult(interp, TclGetString(Tcl_GetObjResult(interp)), | ||
TCL_VOLATILE); | ||
} | ||
Tcl_DecrRefCount(exprPtr); /* discard the expression object */ | ||
} else { | ||
/* | ||
* An empty string. Just set the result boolean to 0 (false). | ||
*/ | ||
*ptr = 0; | ||
} | ||
return result; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* Tcl_ExprLongObj, Tcl_ExprDoubleObj, Tcl_ExprBooleanObj -- | ||
* | ||
* Procedures to evaluate an expression in an object and return its | ||
* value in a particular form. | ||
* | ||
* Results: | ||
* Each of the procedures below returns a standard Tcl result | ||
* object. If an error occurs then an error message is left in the | ||
* interpreter's result. Otherwise the value of the expression, in the | ||
* appropriate form, is stored at *ptr. If the expression had a result | ||
* that was incompatible with the desired form then an error is | ||
* returned. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
int | ||
Tcl_ExprLongObj(interp, objPtr, ptr) | ||
Tcl_Interp *interp; /* Context in which to evaluate the | ||
* expression. */ | ||
register Tcl_Obj *objPtr; /* Expression to evaluate. */ | ||
long *ptr; /* Where to store long result. */ | ||
{ | ||
Tcl_Obj *resultPtr; | ||
int result; | ||
result = Tcl_ExprObj(interp, objPtr, &resultPtr); | ||
if (result == TCL_OK) { | ||
if (resultPtr->typePtr == &tclIntType) { | ||
*ptr = resultPtr->internalRep.longValue; | ||
} else if (resultPtr->typePtr == &tclDoubleType) { | ||
*ptr = (long) resultPtr->internalRep.doubleValue; | ||
} else { | ||
result = Tcl_GetLongFromObj(interp, resultPtr, ptr); | ||
if (result != TCL_OK) { | ||
return result; | ||
} | ||
} | ||
Tcl_DecrRefCount(resultPtr); /* discard the result object */ | ||
} | ||
return result; | ||
} | ||
int | ||
Tcl_ExprDoubleObj(interp, objPtr, ptr) | ||
Tcl_Interp *interp; /* Context in which to evaluate the | ||
* expression. */ | ||
register Tcl_Obj *objPtr; /* Expression to evaluate. */ | ||
double *ptr; /* Where to store double result. */ | ||
{ | ||
Tcl_Obj *resultPtr; | ||
int result; | ||
result = Tcl_ExprObj(interp, objPtr, &resultPtr); | ||
if (result == TCL_OK) { | ||
if (resultPtr->typePtr == &tclIntType) { | ||
*ptr = (double) resultPtr->internalRep.longValue; | ||
} else if (resultPtr->typePtr == &tclDoubleType) { | ||
*ptr = resultPtr->internalRep.doubleValue; | ||
} else { | ||
result = Tcl_GetDoubleFromObj(interp, resultPtr, ptr); | ||
if (result != TCL_OK) { | ||
return result; | ||
} | ||
} | ||
Tcl_DecrRefCount(resultPtr); /* discard the result object */ | ||
} | ||
return result; | ||
} | ||
int | ||
Tcl_ExprBooleanObj(interp, objPtr, ptr) | ||
Tcl_Interp *interp; /* Context in which to evaluate the | ||
* expression. */ | ||
register Tcl_Obj *objPtr; /* Expression to evaluate. */ | ||
int *ptr; /* Where to store 0/1 result. */ | ||
{ | ||
Tcl_Obj *resultPtr; | ||
int result; | ||
result = Tcl_ExprObj(interp, objPtr, &resultPtr); | ||
if (result == TCL_OK) { | ||
if (resultPtr->typePtr == &tclIntType) { | ||
*ptr = (resultPtr->internalRep.longValue != 0); | ||
} else if (resultPtr->typePtr == &tclDoubleType) { | ||
*ptr = (resultPtr->internalRep.doubleValue != 0.0); | ||
} else { | ||
result = Tcl_GetBooleanFromObj(interp, resultPtr, ptr); | ||
} | ||
Tcl_DecrRefCount(resultPtr); /* discard the result object */ | ||
} | ||
return result; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* TclInvoke -- | ||
* | ||
* Invokes a Tcl command, given an argv/argc, from either the | ||
* exposed or the hidden sets of commands in the given interpreter. | ||
* NOTE: The command is invoked in the current stack frame of | ||
* the interpreter, thus it can modify local variables. | ||
* | ||
* Results: | ||
* A standard Tcl result. | ||
* | ||
* Side effects: | ||
* Whatever the command does. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
int | ||
TclInvoke(interp, argc, argv, flags) | ||
Tcl_Interp *interp; /* Where to invoke the command. */ | ||
int argc; /* Count of args. */ | ||
register char **argv; /* The arg strings; argv[0] is the name of | ||
* the command to invoke. */ | ||
int flags; /* Combination of flags controlling the | ||
* call: TCL_INVOKE_HIDDEN and | ||
* TCL_INVOKE_NO_UNKNOWN. */ | ||
{ | ||
register Tcl_Obj *objPtr; | ||
register int i; | ||
int length, result; | ||
/* | ||
* This procedure generates an objv array for object arguments that hold | ||
* the argv strings. It starts out with stack-allocated space but uses | ||
* dynamically-allocated storage if needed. | ||
*/ | ||
#define NUM_ARGS 20 | ||
Tcl_Obj *(objStorage[NUM_ARGS]); | ||
register Tcl_Obj **objv = objStorage; | ||
/* | ||
* Create the object argument array "objv". Make sure objv is large | ||
* enough to hold the objc arguments plus 1 extra for the zero | ||
* end-of-objv word. | ||
*/ | ||
if ((argc + 1) > NUM_ARGS) { | ||
objv = (Tcl_Obj **) | ||
ckalloc((unsigned)(argc + 1) * sizeof(Tcl_Obj *)); | ||
} | ||
for (i = 0; i < argc; i++) { | ||
length = strlen(argv[i]); | ||
objv[i] = Tcl_NewStringObj(argv[i], length); | ||
Tcl_IncrRefCount(objv[i]); | ||
} | ||
objv[argc] = 0; | ||
/* | ||
* Use TclObjInterpProc to actually invoke the command. | ||
*/ | ||
result = TclObjInvoke(interp, argc, objv, flags); | ||
/* | ||
* Move the interpreter's object result to the string result, | ||
* then reset the object result. | ||
*/ | ||
Tcl_SetResult(interp, TclGetString(Tcl_GetObjResult(interp)), | ||
TCL_VOLATILE); | ||
/* | ||
* Decrement the ref counts on the objv elements since we are done | ||
* with them. | ||
*/ | ||
for (i = 0; i < argc; i++) { | ||
objPtr = objv[i]; | ||
Tcl_DecrRefCount(objPtr); | ||
} | ||
/* | ||
* Free the objv array if malloc'ed storage was used. | ||
*/ | ||
if (objv != objStorage) { | ||
ckfree((char *) objv); | ||
} | ||
return result; | ||
#undef NUM_ARGS | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* TclGlobalInvoke -- | ||
* | ||
* Invokes a Tcl command, given an argv/argc, from either the | ||
* exposed or hidden sets of commands in the given interpreter. | ||
* NOTE: The command is invoked in the global stack frame of | ||
* the interpreter, thus it cannot see any current state on | ||
* the stack for that interpreter. | ||
* | ||
* Results: | ||
* A standard Tcl result. | ||
* | ||
* Side effects: | ||
* Whatever the command does. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
int | ||
TclGlobalInvoke(interp, argc, argv, flags) | ||
Tcl_Interp *interp; /* Where to invoke the command. */ | ||
int argc; /* Count of args. */ | ||
register char **argv; /* The arg strings; argv[0] is the name of | ||
* the command to invoke. */ | ||
int flags; /* Combination of flags controlling the | ||
* call: TCL_INVOKE_HIDDEN and | ||
* TCL_INVOKE_NO_UNKNOWN. */ | ||
{ | ||
register Interp *iPtr = (Interp *) interp; | ||
int result; | ||
CallFrame *savedVarFramePtr; | ||
savedVarFramePtr = iPtr->varFramePtr; | ||
iPtr->varFramePtr = NULL; | ||
result = TclInvoke(interp, argc, argv, flags); | ||
iPtr->varFramePtr = savedVarFramePtr; | ||
return result; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* TclObjInvokeGlobal -- | ||
* | ||
* Object version: Invokes a Tcl command, given an objv/objc, from | ||
* either the exposed or hidden set of commands in the given | ||
* interpreter. | ||
* NOTE: The command is invoked in the global stack frame of the | ||
* interpreter, thus it cannot see any current state on the | ||
* stack of that interpreter. | ||
* | ||
* Results: | ||
* A standard Tcl result. | ||
* | ||
* Side effects: | ||
* Whatever the command does. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
int | ||
TclObjInvokeGlobal(interp, objc, objv, flags) | ||
Tcl_Interp *interp; /* Interpreter in which command is to be | ||
* invoked. */ | ||
int objc; /* Count of arguments. */ | ||
Tcl_Obj *CONST objv[]; /* Argument objects; objv[0] points to the | ||
* name of the command to invoke. */ | ||
int flags; /* Combination of flags controlling the | ||
* call: TCL_INVOKE_HIDDEN, | ||
* TCL_INVOKE_NO_UNKNOWN, or | ||
* TCL_INVOKE_NO_TRACEBACK. */ | ||
{ | ||
register Interp *iPtr = (Interp *) interp; | ||
int result; | ||
CallFrame *savedVarFramePtr; | ||
savedVarFramePtr = iPtr->varFramePtr; | ||
iPtr->varFramePtr = NULL; | ||
result = TclObjInvoke(interp, objc, objv, flags); | ||
iPtr->varFramePtr = savedVarFramePtr; | ||
return result; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* TclObjInvoke -- | ||
* | ||
* Invokes a Tcl command, given an objv/objc, from either the | ||
* exposed or the hidden sets of commands in the given interpreter. | ||
* | ||
* Results: | ||
* A standard Tcl object result. | ||
* | ||
* Side effects: | ||
* Whatever the command does. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
int | ||
TclObjInvoke(interp, objc, objv, flags) | ||
Tcl_Interp *interp; /* Interpreter in which command is to be | ||
* invoked. */ | ||
int objc; /* Count of arguments. */ | ||
Tcl_Obj *CONST objv[]; /* Argument objects; objv[0] points to the | ||
* name of the command to invoke. */ | ||
int flags; /* Combination of flags controlling the | ||
* call: TCL_INVOKE_HIDDEN, | ||
* TCL_INVOKE_NO_UNKNOWN, or | ||
* TCL_INVOKE_NO_TRACEBACK. */ | ||
{ | ||
register Interp *iPtr = (Interp *) interp; | ||
Tcl_HashTable *hTblPtr; /* Table of hidden commands. */ | ||
char *cmdName; /* Name of the command from objv[0]. */ | ||
register Tcl_HashEntry *hPtr; | ||
Tcl_Command cmd; | ||
Command *cmdPtr; | ||
int localObjc; /* Used to invoke "unknown" if the */ | ||
Tcl_Obj **localObjv = NULL; /* command is not found. */ | ||
register int i; | ||
int length, result; | ||
char *bytes; | ||
if (interp == (Tcl_Interp *) NULL) { | ||
return TCL_ERROR; | ||
} | ||
if ((objc < 1) || (objv == (Tcl_Obj **) NULL)) { | ||
Tcl_AppendToObj(Tcl_GetObjResult(interp), | ||
"illegal argument vector", -1); | ||
return TCL_ERROR; | ||
} | ||
cmdName = Tcl_GetString(objv[0]); | ||
if (flags & TCL_INVOKE_HIDDEN) { | ||
/* | ||
* We never invoke "unknown" for hidden commands. | ||
*/ | ||
hPtr = NULL; | ||
hTblPtr = ((Interp *) interp)->hiddenCmdTablePtr; | ||
if (hTblPtr != NULL) { | ||
hPtr = Tcl_FindHashEntry(hTblPtr, cmdName); | ||
} | ||
if (hPtr == NULL) { | ||
Tcl_ResetResult(interp); | ||
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), | ||
"invalid hidden command name \"", cmdName, "\"", | ||
(char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
cmdPtr = (Command *) Tcl_GetHashValue(hPtr); | ||
} else { | ||
cmdPtr = NULL; | ||
cmd = Tcl_FindCommand(interp, cmdName, | ||
(Tcl_Namespace *) NULL, /*flags*/ TCL_GLOBAL_ONLY); | ||
if (cmd != (Tcl_Command) NULL) { | ||
cmdPtr = (Command *) cmd; | ||
} | ||
if (cmdPtr == NULL) { | ||
if (!(flags & TCL_INVOKE_NO_UNKNOWN)) { | ||
cmd = Tcl_FindCommand(interp, "unknown", | ||
(Tcl_Namespace *) NULL, /*flags*/ TCL_GLOBAL_ONLY); | ||
if (cmd != (Tcl_Command) NULL) { | ||
cmdPtr = (Command *) cmd; | ||
} | ||
if (cmdPtr != NULL) { | ||
localObjc = (objc + 1); | ||
localObjv = (Tcl_Obj **) | ||
ckalloc((unsigned) (sizeof(Tcl_Obj *) * localObjc)); | ||
localObjv[0] = Tcl_NewStringObj("unknown", -1); | ||
Tcl_IncrRefCount(localObjv[0]); | ||
for (i = 0; i < objc; i++) { | ||
localObjv[i+1] = objv[i]; | ||
} | ||
objc = localObjc; | ||
objv = localObjv; | ||
} | ||
} | ||
/* | ||
* Check again if we found the command. If not, "unknown" is | ||
* not present and we cannot help, or the caller said not to | ||
* call "unknown" (they specified TCL_INVOKE_NO_UNKNOWN). | ||
*/ | ||
if (cmdPtr == NULL) { | ||
Tcl_ResetResult(interp); | ||
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), | ||
"invalid command name \"", cmdName, "\"", | ||
(char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
} | ||
} | ||
/* | ||
* Invoke the command procedure. First reset the interpreter's string | ||
* and object results to their default empty values since they could | ||
* have gotten changed by earlier invocations. | ||
*/ | ||
Tcl_ResetResult(interp); | ||
iPtr->cmdCount++; | ||
result = (*cmdPtr->objProc)(cmdPtr->objClientData, interp, objc, objv); | ||
/* | ||
* If an error occurred, record information about what was being | ||
* executed when the error occurred. | ||
*/ | ||
if ((result == TCL_ERROR) | ||
&& ((flags & TCL_INVOKE_NO_TRACEBACK) == 0) | ||
&& ((iPtr->flags & ERR_ALREADY_LOGGED) == 0)) { | ||
Tcl_DString ds; | ||
Tcl_DStringInit(&ds); | ||
if (!(iPtr->flags & ERR_IN_PROGRESS)) { | ||
Tcl_DStringAppend(&ds, "\n while invoking\n\"", -1); | ||
} else { | ||
Tcl_DStringAppend(&ds, "\n invoked from within\n\"", -1); | ||
} | ||
for (i = 0; i < objc; i++) { | ||
bytes = Tcl_GetStringFromObj(objv[i], &length); | ||
Tcl_DStringAppend(&ds, bytes, length); | ||
if (i < (objc - 1)) { | ||
Tcl_DStringAppend(&ds, " ", -1); | ||
} else if (Tcl_DStringLength(&ds) > 100) { | ||
Tcl_DStringSetLength(&ds, 100); | ||
Tcl_DStringAppend(&ds, "...", -1); | ||
break; | ||
} | ||
} | ||
Tcl_DStringAppend(&ds, "\"", -1); | ||
Tcl_AddObjErrorInfo(interp, Tcl_DStringValue(&ds), -1); | ||
Tcl_DStringFree(&ds); | ||
iPtr->flags &= ~ERR_ALREADY_LOGGED; | ||
} | ||
/* | ||
* Free any locally allocated storage used to call "unknown". | ||
*/ | ||
if (localObjv != (Tcl_Obj **) NULL) { | ||
Tcl_DecrRefCount(localObjv[0]); | ||
ckfree((char *) localObjv); | ||
} | ||
return result; | ||
} | ||
/* | ||
*--------------------------------------------------------------------------- | ||
* | ||
* Tcl_ExprString -- | ||
* | ||
* Evaluate an expression in a string and return its value in string | ||
* form. | ||
* | ||
* Results: | ||
* A standard Tcl result. If the result is TCL_OK, then the interp's | ||
* result is set to the string value of the expression. If the result | ||
* is TCL_ERROR, then the interp's result contains an error message. | ||
* | ||
* Side effects: | ||
* A Tcl object is allocated to hold a copy of the expression string. | ||
* This expression object is passed to Tcl_ExprObj and then | ||
* deallocated. | ||
* | ||
*--------------------------------------------------------------------------- | ||
*/ | ||
int | ||
Tcl_ExprString(interp, string) | ||
Tcl_Interp *interp; /* Context in which to evaluate the | ||
* expression. */ | ||
char *string; /* Expression to evaluate. */ | ||
{ | ||
register Tcl_Obj *exprPtr; | ||
Tcl_Obj *resultPtr; | ||
int length = strlen(string); | ||
char buf[TCL_DOUBLE_SPACE]; | ||
int result = TCL_OK; | ||
if (length > 0) { | ||
TclNewObj(exprPtr); | ||
TclInitStringRep(exprPtr, string, length); | ||
Tcl_IncrRefCount(exprPtr); | ||
result = Tcl_ExprObj(interp, exprPtr, &resultPtr); | ||
if (result == TCL_OK) { | ||
/* | ||
* Set the interpreter's string result from the result object. | ||
*/ | ||
if (resultPtr->typePtr == &tclIntType) { | ||
sprintf(buf, "%ld", resultPtr->internalRep.longValue); | ||
Tcl_SetResult(interp, buf, TCL_VOLATILE); | ||
} else if (resultPtr->typePtr == &tclDoubleType) { | ||
Tcl_PrintDouble((Tcl_Interp *) NULL, | ||
resultPtr->internalRep.doubleValue, buf); | ||
Tcl_SetResult(interp, buf, TCL_VOLATILE); | ||
} else { | ||
/* | ||
* Set interpreter's string result from the result object. | ||
*/ | ||
Tcl_SetResult(interp, TclGetString(resultPtr), | ||
TCL_VOLATILE); | ||
} | ||
Tcl_DecrRefCount(resultPtr); /* discard the result object */ | ||
} else { | ||
/* | ||
* Move the interpreter's object result to the string result, | ||
* then reset the object result. | ||
*/ | ||
Tcl_SetResult(interp, TclGetString(Tcl_GetObjResult(interp)), | ||
TCL_VOLATILE); | ||
} | ||
Tcl_DecrRefCount(exprPtr); /* discard the expression object */ | ||
} else { | ||
/* | ||
* An empty string. Just set the interpreter's result to 0. | ||
*/ | ||
Tcl_SetResult(interp, "0", TCL_VOLATILE); | ||
} | ||
return result; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* Tcl_ExprObj -- | ||
* | ||
* Evaluate an expression in a Tcl_Obj. | ||
* | ||
* Results: | ||
* A standard Tcl object result. If the result is other than TCL_OK, | ||
* then the interpreter's result contains an error message. If the | ||
* result is TCL_OK, then a pointer to the expression's result value | ||
* object is stored in resultPtrPtr. In that case, the object's ref | ||
* count is incremented to reflect the reference returned to the | ||
* caller; the caller is then responsible for the resulting object | ||
* and must, for example, decrement the ref count when it is finished | ||
* with the object. | ||
* | ||
* Side effects: | ||
* Any side effects caused by subcommands in the expression, if any. | ||
* The interpreter result is not modified unless there is an error. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
int | ||
Tcl_ExprObj(interp, objPtr, resultPtrPtr) | ||
Tcl_Interp *interp; /* Context in which to evaluate the | ||
* expression. */ | ||
register Tcl_Obj *objPtr; /* Points to Tcl object containing | ||
* expression to evaluate. */ | ||
Tcl_Obj **resultPtrPtr; /* Where the Tcl_Obj* that is the expression | ||
* result is stored if no errors occur. */ | ||
{ | ||
Interp *iPtr = (Interp *) interp; | ||
CompileEnv compEnv; /* Compilation environment structure | ||
* allocated in frame. */ | ||
LiteralTable *localTablePtr = &(compEnv.localLitTable); | ||
register ByteCode *codePtr = NULL; | ||
/* Tcl Internal type of bytecode. | ||
* Initialized to avoid compiler warning. */ | ||
AuxData *auxDataPtr; | ||
LiteralEntry *entryPtr; | ||
Tcl_Obj *saveObjPtr; | ||
char *string; | ||
int length, i, result; | ||
/* | ||
* First handle some common expressions specially. | ||
*/ | ||
string = Tcl_GetStringFromObj(objPtr, &length); | ||
if (length == 1) { | ||
if (*string == '0') { | ||
*resultPtrPtr = Tcl_NewLongObj(0); | ||
Tcl_IncrRefCount(*resultPtrPtr); | ||
return TCL_OK; | ||
} else if (*string == '1') { | ||
*resultPtrPtr = Tcl_NewLongObj(1); | ||
Tcl_IncrRefCount(*resultPtrPtr); | ||
return TCL_OK; | ||
} | ||
} else if ((length == 2) && (*string == '!')) { | ||
if (*(string+1) == '0') { | ||
*resultPtrPtr = Tcl_NewLongObj(1); | ||
Tcl_IncrRefCount(*resultPtrPtr); | ||
return TCL_OK; | ||
} else if (*(string+1) == '1') { | ||
*resultPtrPtr = Tcl_NewLongObj(0); | ||
Tcl_IncrRefCount(*resultPtrPtr); | ||
return TCL_OK; | ||
} | ||
} | ||
/* | ||
* Get the ByteCode from the object. If it exists, make sure it hasn't | ||
* been invalidated by, e.g., someone redefining a command with a | ||
* compile procedure (this might make the compiled code wrong). If | ||
* necessary, convert the object to be a ByteCode object and compile it. | ||
* Also, if the code was compiled in/for a different interpreter, we | ||
* recompile it. | ||
* | ||
* Precompiled expressions, however, are immutable and therefore | ||
* they are not recompiled, even if the epoch has changed. | ||
* | ||
*/ | ||
if (objPtr->typePtr == &tclByteCodeType) { | ||
codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr; | ||
if (((Interp *) *codePtr->interpHandle != iPtr) | ||
|| (codePtr->compileEpoch != iPtr->compileEpoch)) { | ||
if (codePtr->flags & TCL_BYTECODE_PRECOMPILED) { | ||
if ((Interp *) *codePtr->interpHandle != iPtr) { | ||
panic("Tcl_ExprObj: compiled expression jumped interps"); | ||
} | ||
codePtr->compileEpoch = iPtr->compileEpoch; | ||
} else { | ||
(*tclByteCodeType.freeIntRepProc)(objPtr); | ||
objPtr->typePtr = (Tcl_ObjType *) NULL; | ||
} | ||
} | ||
} | ||
if (objPtr->typePtr != &tclByteCodeType) { | ||
TclInitCompileEnv(interp, &compEnv, string, length); | ||
result = TclCompileExpr(interp, string, length, &compEnv); | ||
/* | ||
* Free the compilation environment's literal table bucket array if | ||
* it was dynamically allocated. | ||
*/ | ||
if (localTablePtr->buckets != localTablePtr->staticBuckets) { | ||
ckfree((char *) localTablePtr->buckets); | ||
} | ||
if (result != TCL_OK) { | ||
/* | ||
* Compilation errors. Free storage allocated for compilation. | ||
*/ | ||
#ifdef TCL_COMPILE_DEBUG | ||
TclVerifyLocalLiteralTable(&compEnv); | ||
#endif /*TCL_COMPILE_DEBUG*/ | ||
entryPtr = compEnv.literalArrayPtr; | ||
for (i = 0; i < compEnv.literalArrayNext; i++) { | ||
TclReleaseLiteral(interp, entryPtr->objPtr); | ||
entryPtr++; | ||
} | ||
#ifdef TCL_COMPILE_DEBUG | ||
TclVerifyGlobalLiteralTable(iPtr); | ||
#endif /*TCL_COMPILE_DEBUG*/ | ||
auxDataPtr = compEnv.auxDataArrayPtr; | ||
for (i = 0; i < compEnv.auxDataArrayNext; i++) { | ||
if (auxDataPtr->type->freeProc != NULL) { | ||
auxDataPtr->type->freeProc(auxDataPtr->clientData); | ||
} | ||
auxDataPtr++; | ||
} | ||
TclFreeCompileEnv(&compEnv); | ||
return result; | ||
} | ||
/* | ||
* Successful compilation. If the expression yielded no | ||
* instructions, push an zero object as the expression's result. | ||
*/ | ||
if (compEnv.codeNext == compEnv.codeStart) { | ||
TclEmitPush(TclRegisterLiteral(&compEnv, "0", 1, /*onHeap*/ 0), | ||
&compEnv); | ||
} | ||
/* | ||
* Add a "done" instruction as the last instruction and change the | ||
* object into a ByteCode object. Ownership of the literal objects | ||
* and aux data items is given to the ByteCode object. | ||
*/ | ||
compEnv.numSrcBytes = iPtr->termOffset; | ||
TclEmitOpcode(INST_DONE, &compEnv); | ||
TclInitByteCodeObj(objPtr, &compEnv); | ||
TclFreeCompileEnv(&compEnv); | ||
codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr; | ||
#ifdef TCL_COMPILE_DEBUG | ||
if (tclTraceCompile == 2) { | ||
TclPrintByteCodeObj(interp, objPtr); | ||
} | ||
#endif /* TCL_COMPILE_DEBUG */ | ||
} | ||
/* | ||
* Execute the expression after first saving the interpreter's result. | ||
*/ | ||
saveObjPtr = Tcl_GetObjResult(interp); | ||
Tcl_IncrRefCount(saveObjPtr); | ||
Tcl_ResetResult(interp); | ||
/* | ||
* Increment the code's ref count while it is being executed. If | ||
* afterwards no references to it remain, free the code. | ||
*/ | ||
codePtr->refCount++; | ||
result = TclExecuteByteCode(interp, codePtr); | ||
codePtr->refCount--; | ||
if (codePtr->refCount <= 0) { | ||
TclCleanupByteCode(codePtr); | ||
objPtr->typePtr = NULL; | ||
objPtr->internalRep.otherValuePtr = NULL; | ||
} | ||
/* | ||
* If the expression evaluated successfully, store a pointer to its | ||
* value object in resultPtrPtr then restore the old interpreter result. | ||
* We increment the object's ref count to reflect the reference that we | ||
* are returning to the caller. We also decrement the ref count of the | ||
* interpreter's result object after calling Tcl_SetResult since we | ||
* next store into that field directly. | ||
*/ | ||
if (result == TCL_OK) { | ||
*resultPtrPtr = iPtr->objResultPtr; | ||
Tcl_IncrRefCount(iPtr->objResultPtr); | ||
Tcl_SetObjResult(interp, saveObjPtr); | ||
} | ||
Tcl_DecrRefCount(saveObjPtr); | ||
return result; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_CreateTrace -- | ||
* | ||
* Arrange for a procedure to be called to trace command execution. | ||
* | ||
* Results: | ||
* The return value is a token for the trace, which may be passed | ||
* to Tcl_DeleteTrace to eliminate the trace. | ||
* | ||
* Side effects: | ||
* From now on, proc will be called just before a command procedure | ||
* is called to execute a Tcl command. Calls to proc will have the | ||
* following form: | ||
* | ||
* void | ||
* proc(clientData, interp, level, command, cmdProc, cmdClientData, | ||
* argc, argv) | ||
* ClientData clientData; | ||
* Tcl_Interp *interp; | ||
* int level; | ||
* char *command; | ||
* int (*cmdProc)(); | ||
* ClientData cmdClientData; | ||
* int argc; | ||
* char **argv; | ||
* { | ||
* } | ||
* | ||
* The clientData and interp arguments to proc will be the same | ||
* as the corresponding arguments to this procedure. Level gives | ||
* the nesting level of command interpretation for this interpreter | ||
* (0 corresponds to top level). Command gives the ASCII text of | ||
* the raw command, cmdProc and cmdClientData give the procedure that | ||
* will be called to process the command and the ClientData value it | ||
* will receive, and argc and argv give the arguments to the | ||
* command, after any argument parsing and substitution. Proc | ||
* does not return a value. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
Tcl_Trace | ||
Tcl_CreateTrace(interp, level, proc, clientData) | ||
Tcl_Interp *interp; /* Interpreter in which to create trace. */ | ||
int level; /* Only call proc for commands at nesting | ||
* level<=argument level (1=>top level). */ | ||
Tcl_CmdTraceProc *proc; /* Procedure to call before executing each | ||
* command. */ | ||
ClientData clientData; /* Arbitrary value word to pass to proc. */ | ||
{ | ||
register Trace *tracePtr; | ||
register Interp *iPtr = (Interp *) interp; | ||
/* | ||
* Invalidate existing compiled code for this interpreter and arrange | ||
* (by setting the DONT_COMPILE_CMDS_INLINE flag) that when compiling | ||
* new code, no commands will be compiled inline (i.e., into an inline | ||
* sequence of instructions). We do this because commands that were | ||
* compiled inline will never result in a command trace being called. | ||
*/ | ||
iPtr->compileEpoch++; | ||
iPtr->flags |= DONT_COMPILE_CMDS_INLINE; | ||
tracePtr = (Trace *) ckalloc(sizeof(Trace)); | ||
tracePtr->level = level; | ||
tracePtr->proc = proc; | ||
tracePtr->clientData = clientData; | ||
tracePtr->nextPtr = iPtr->tracePtr; | ||
iPtr->tracePtr = tracePtr; | ||
return (Tcl_Trace) tracePtr; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_DeleteTrace -- | ||
* | ||
* Remove a trace. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* From now on there will be no more calls to the procedure given | ||
* in trace. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
void | ||
Tcl_DeleteTrace(interp, trace) | ||
Tcl_Interp *interp; /* Interpreter that contains trace. */ | ||
Tcl_Trace trace; /* Token for trace (returned previously by | ||
* Tcl_CreateTrace). */ | ||
{ | ||
register Interp *iPtr = (Interp *) interp; | ||
register Trace *tracePtr = (Trace *) trace; | ||
register Trace *tracePtr2; | ||
if (iPtr->tracePtr == tracePtr) { | ||
iPtr->tracePtr = tracePtr->nextPtr; | ||
ckfree((char *) tracePtr); | ||
} else { | ||
for (tracePtr2 = iPtr->tracePtr; tracePtr2 != NULL; | ||
tracePtr2 = tracePtr2->nextPtr) { | ||
if (tracePtr2->nextPtr == tracePtr) { | ||
tracePtr2->nextPtr = tracePtr->nextPtr; | ||
ckfree((char *) tracePtr); | ||
break; | ||
} | ||
} | ||
} | ||
if (iPtr->tracePtr == NULL) { | ||
/* | ||
* When compiling new code, allow commands to be compiled inline. | ||
*/ | ||
iPtr->flags &= ~DONT_COMPILE_CMDS_INLINE; | ||
} | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_AddErrorInfo -- | ||
* | ||
* Add information to the "errorInfo" variable that describes the | ||
* current error. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* The contents of message are added to the "errorInfo" variable. | ||
* If Tcl_Eval has been called since the current value of errorInfo | ||
* was set, errorInfo is cleared before adding the new message. | ||
* If we are just starting to log an error, errorInfo is initialized | ||
* from the error message in the interpreter's result. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
void | ||
Tcl_AddErrorInfo(interp, message) | ||
Tcl_Interp *interp; /* Interpreter to which error information | ||
* pertains. */ | ||
CONST char *message; /* Message to record. */ | ||
{ | ||
Tcl_AddObjErrorInfo(interp, message, -1); | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_AddObjErrorInfo -- | ||
* | ||
* Add information to the "errorInfo" variable that describes the | ||
* current error. This routine differs from Tcl_AddErrorInfo by | ||
* taking a byte pointer and length. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* "length" bytes from "message" are added to the "errorInfo" variable. | ||
* If "length" is negative, use bytes up to the first NULL byte. | ||
* If Tcl_EvalObj has been called since the current value of errorInfo | ||
* was set, errorInfo is cleared before adding the new message. | ||
* If we are just starting to log an error, errorInfo is initialized | ||
* from the error message in the interpreter's result. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
void | ||
Tcl_AddObjErrorInfo(interp, message, length) | ||
Tcl_Interp *interp; /* Interpreter to which error information | ||
* pertains. */ | ||
CONST char *message; /* Points to the first byte of an array of | ||
* bytes of the message. */ | ||
int length; /* The number of bytes in the message. | ||
* If < 0, then append all bytes up to a | ||
* NULL byte. */ | ||
{ | ||
register Interp *iPtr = (Interp *) interp; | ||
Tcl_Obj *messagePtr; | ||
/* | ||
* If we are just starting to log an error, errorInfo is initialized | ||
* from the error message in the interpreter's result. | ||
*/ | ||
if (!(iPtr->flags & ERR_IN_PROGRESS)) { /* just starting to log error */ | ||
iPtr->flags |= ERR_IN_PROGRESS; | ||
if (iPtr->result[0] == 0) { | ||
(void) Tcl_SetVar2Ex(interp, "errorInfo", NULL, iPtr->objResultPtr, | ||
TCL_GLOBAL_ONLY); | ||
} else { /* use the string result */ | ||
Tcl_SetVar2(interp, "errorInfo", (char *) NULL, interp->result, | ||
TCL_GLOBAL_ONLY); | ||
} | ||
/* | ||
* If the errorCode variable wasn't set by the code that generated | ||
* the error, set it to "NONE". | ||
*/ | ||
if (!(iPtr->flags & ERROR_CODE_SET)) { | ||
(void) Tcl_SetVar2(interp, "errorCode", (char *) NULL, "NONE", | ||
TCL_GLOBAL_ONLY); | ||
} | ||
} | ||
/* | ||
* Now append "message" to the end of errorInfo. | ||
*/ | ||
if (length != 0) { | ||
messagePtr = Tcl_NewStringObj(message, length); | ||
Tcl_IncrRefCount(messagePtr); | ||
Tcl_SetVar2Ex(interp, "errorInfo", NULL, messagePtr, | ||
(TCL_GLOBAL_ONLY | TCL_APPEND_VALUE)); | ||
Tcl_DecrRefCount(messagePtr); /* free msg object appended above */ | ||
} | ||
} | ||
/* | ||
*--------------------------------------------------------------------------- | ||
* | ||
* Tcl_VarEvalVA -- | ||
* | ||
* Given a variable number of string arguments, concatenate them | ||
* all together and execute the result as a Tcl command. | ||
* | ||
* Results: | ||
* A standard Tcl return result. An error message or other result may | ||
* be left in the interp's result. | ||
* | ||
* Side effects: | ||
* Depends on what was done by the command. | ||
* | ||
*--------------------------------------------------------------------------- | ||
*/ | ||
int | ||
Tcl_VarEvalVA (interp, argList) | ||
Tcl_Interp *interp; /* Interpreter in which to evaluate command. */ | ||
va_list argList; /* Variable argument list. */ | ||
{ | ||
Tcl_DString buf; | ||
char *string; | ||
int result; | ||
/* | ||
* Copy the strings one after the other into a single larger | ||
* string. Use stack-allocated space for small commands, but if | ||
* the command gets too large than call ckalloc to create the | ||
* space. | ||
*/ | ||
Tcl_DStringInit(&buf); | ||
while (1) { | ||
string = va_arg(argList, char *); | ||
if (string == NULL) { | ||
break; | ||
} | ||
Tcl_DStringAppend(&buf, string, -1); | ||
} | ||
result = Tcl_Eval(interp, Tcl_DStringValue(&buf)); | ||
Tcl_DStringFree(&buf); | ||
return result; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_VarEval -- | ||
* | ||
* Given a variable number of string arguments, concatenate them | ||
* all together and execute the result as a Tcl command. | ||
* | ||
* Results: | ||
* A standard Tcl return result. An error message or other | ||
* result may be left in interp->result. | ||
* | ||
* Side effects: | ||
* Depends on what was done by the command. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
/* VARARGS2 */ /* ARGSUSED */ | ||
int | ||
Tcl_VarEval TCL_VARARGS_DEF(Tcl_Interp *,arg1) | ||
{ | ||
Tcl_Interp *interp; | ||
va_list argList; | ||
int result; | ||
interp = TCL_VARARGS_START(Tcl_Interp *,arg1,argList); | ||
result = Tcl_VarEvalVA(interp, argList); | ||
va_end(argList); | ||
return result; | ||
} | ||
/* | ||
*--------------------------------------------------------------------------- | ||
* | ||
* Tcl_GlobalEval -- | ||
* | ||
* Evaluate a command at global level in an interpreter. | ||
* | ||
* Results: | ||
* A standard Tcl result is returned, and the interp's result is | ||
* modified accordingly. | ||
* | ||
* Side effects: | ||
* The command string is executed in interp, and the execution | ||
* is carried out in the variable context of global level (no | ||
* procedures active), just as if an "uplevel #0" command were | ||
* being executed. | ||
* | ||
--------------------------------------------------------------------------- | ||
*/ | ||
int | ||
Tcl_GlobalEval(interp, command) | ||
Tcl_Interp *interp; /* Interpreter in which to evaluate command. */ | ||
char *command; /* Command to evaluate. */ | ||
{ | ||
register Interp *iPtr = (Interp *) interp; | ||
int result; | ||
CallFrame *savedVarFramePtr; | ||
savedVarFramePtr = iPtr->varFramePtr; | ||
iPtr->varFramePtr = NULL; | ||
result = Tcl_Eval(interp, command); | ||
iPtr->varFramePtr = savedVarFramePtr; | ||
return result; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_SetRecursionLimit -- | ||
* | ||
* Set the maximum number of recursive calls that may be active | ||
* for an interpreter at once. | ||
* | ||
* Results: | ||
* The return value is the old limit on nesting for interp. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
int | ||
Tcl_SetRecursionLimit(interp, depth) | ||
Tcl_Interp *interp; /* Interpreter whose nesting limit | ||
* is to be set. */ | ||
int depth; /* New value for maximimum depth. */ | ||
{ | ||
Interp *iPtr = (Interp *) interp; | ||
int old; | ||
old = iPtr->maxNestingDepth; | ||
if (depth > 0) { | ||
iPtr->maxNestingDepth = depth; | ||
} | ||
return old; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_AllowExceptions -- | ||
* | ||
* Sets a flag in an interpreter so that exceptions can occur | ||
* in the next call to Tcl_Eval without them being turned into | ||
* errors. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* The TCL_ALLOW_EXCEPTIONS flag gets set in the interpreter's | ||
* evalFlags structure. See the reference documentation for | ||
* more details. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
void | ||
Tcl_AllowExceptions(interp) | ||
Tcl_Interp *interp; /* Interpreter in which to set flag. */ | ||
{ | ||
Interp *iPtr = (Interp *) interp; | ||
iPtr->evalFlags |= TCL_ALLOW_EXCEPTIONS; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* Tcl_GetVersion | ||
* | ||
* Get the Tcl major, minor, and patchlevel version numbers and | ||
* the release type. A patch is a release type TCL_FINAL_RELEASE | ||
* with a patchLevel > 0. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
void Tcl_GetVersion(majorV, minorV, patchLevelV, type) | ||
int *majorV; | ||
int *minorV; | ||
int *patchLevelV; | ||
int *type; | ||
{ | ||
if (majorV != NULL) { | ||
*majorV = TCL_MAJOR_VERSION; | ||
} | ||
if (minorV != NULL) { | ||
*minorV = TCL_MINOR_VERSION; | ||
} | ||
if (patchLevelV != NULL) { | ||
*patchLevelV = TCL_RELEASE_SERIAL; | ||
} | ||
if (type != NULL) { | ||
*type = TCL_RELEASE_LEVEL; | ||
} | ||
} | ||
/* $History: tclbasic.c $ | ||
* | ||
* ***************** Version 1 ***************** | ||
* User: Dtashley Date: 1/02/01 Time: 1:34a | ||
* Created in $/IjuScripter, IjuConsole/Source/Tcl Base | ||
* Initial check-in. | ||
*/ | ||
/* End of TCLBASIC.C */ | ||
1 | /* $Header$ */ | |
2 | /* | |
3 | * tclBasic.c -- | |
4 | * | |
5 | * Contains the basic facilities for TCL command interpretation, | |
6 | * including interpreter creation and deletion, command creation | |
7 | * and deletion, and command parsing and execution. | |
8 | * | |
9 | * Copyright (c) 1987-1994 The Regents of the University of California. | |
10 | * Copyright (c) 1994-1997 Sun Microsystems, Inc. | |
11 | * Copyright (c) 1998-1999 by Scriptics Corporation. | |
12 | * | |
13 | * See the file "license.terms" for information on usage and redistribution | |
14 | * of this file, and for a DISCLAIMER OF ALL WARRANTIES. | |
15 | * | |
16 | * RCS: @(#) $Id: tclbasic.c,v 1.1.1.1 2001/06/13 04:33:43 dtashley Exp $ | |
17 | */ | |
18 | ||
19 | #include "tclInt.h" | |
20 | #include "tclCompile.h" | |
21 | #ifndef TCL_GENERIC_ONLY | |
22 | # include "tclPort.h" | |
23 | #endif | |
24 | ||
25 | /* | |
26 | * Static procedures in this file: | |
27 | */ | |
28 | ||
29 | static void DeleteInterpProc _ANSI_ARGS_((Tcl_Interp *interp)); | |
30 | static void ProcessUnexpectedResult _ANSI_ARGS_(( | |
31 | Tcl_Interp *interp, int returnCode)); | |
32 | static void RecordTracebackInfo _ANSI_ARGS_(( | |
33 | Tcl_Interp *interp, Tcl_Obj *objPtr, | |
34 | int numSrcBytes)); | |
35 | ||
36 | extern TclStubs tclStubs; | |
37 | ||
38 | /* | |
39 | * The following structure defines the commands in the Tcl core. | |
40 | */ | |
41 | ||
42 | typedef struct { | |
43 | char *name; /* Name of object-based command. */ | |
44 | Tcl_CmdProc *proc; /* String-based procedure for command. */ | |
45 | Tcl_ObjCmdProc *objProc; /* Object-based procedure for command. */ | |
46 | CompileProc *compileProc; /* Procedure called to compile command. */ | |
47 | int isSafe; /* If non-zero, command will be present | |
48 | * in safe interpreter. Otherwise it will | |
49 | * be hidden. */ | |
50 | } CmdInfo; | |
51 | ||
52 | /* | |
53 | * The built-in commands, and the procedures that implement them: | |
54 | */ | |
55 | ||
56 | static CmdInfo builtInCmds[] = { | |
57 | /* | |
58 | * Commands in the generic core. Note that at least one of the proc or | |
59 | * objProc members should be non-NULL. This avoids infinitely recursive | |
60 | * calls between TclInvokeObjectCommand and TclInvokeStringCommand if a | |
61 | * command name is computed at runtime and results in the name of a | |
62 | * compiled command. | |
63 | */ | |
64 | ||
65 | {"append", (Tcl_CmdProc *) NULL, Tcl_AppendObjCmd, | |
66 | (CompileProc *) NULL, 1}, | |
67 | {"array", (Tcl_CmdProc *) NULL, Tcl_ArrayObjCmd, | |
68 | (CompileProc *) NULL, 1}, | |
69 | {"binary", (Tcl_CmdProc *) NULL, Tcl_BinaryObjCmd, | |
70 | (CompileProc *) NULL, 1}, | |
71 | {"break", (Tcl_CmdProc *) NULL, Tcl_BreakObjCmd, | |
72 | TclCompileBreakCmd, 1}, | |
73 | {"case", (Tcl_CmdProc *) NULL, Tcl_CaseObjCmd, | |
74 | (CompileProc *) NULL, 1}, | |
75 | {"catch", (Tcl_CmdProc *) NULL, Tcl_CatchObjCmd, | |
76 | TclCompileCatchCmd, 1}, | |
77 | {"clock", (Tcl_CmdProc *) NULL, Tcl_ClockObjCmd, | |
78 | (CompileProc *) NULL, 1}, | |
79 | {"concat", (Tcl_CmdProc *) NULL, Tcl_ConcatObjCmd, | |
80 | (CompileProc *) NULL, 1}, | |
81 | {"continue", (Tcl_CmdProc *) NULL, Tcl_ContinueObjCmd, | |
82 | TclCompileContinueCmd, 1}, | |
83 | {"encoding", (Tcl_CmdProc *) NULL, Tcl_EncodingObjCmd, | |
84 | (CompileProc *) NULL, 0}, | |
85 | {"error", (Tcl_CmdProc *) NULL, Tcl_ErrorObjCmd, | |
86 | (CompileProc *) NULL, 1}, | |
87 | {"eval", (Tcl_CmdProc *) NULL, Tcl_EvalObjCmd, | |
88 | (CompileProc *) NULL, 1}, | |
89 | {"exit", (Tcl_CmdProc *) NULL, Tcl_ExitObjCmd, | |
90 | (CompileProc *) NULL, 0}, | |
91 | {"expr", (Tcl_CmdProc *) NULL, Tcl_ExprObjCmd, | |
92 | TclCompileExprCmd, 1}, | |
93 | {"fcopy", (Tcl_CmdProc *) NULL, Tcl_FcopyObjCmd, | |
94 | (CompileProc *) NULL, 1}, | |
95 | {"fileevent", (Tcl_CmdProc *) NULL, Tcl_FileEventObjCmd, | |
96 | (CompileProc *) NULL, 1}, | |
97 | {"for", (Tcl_CmdProc *) NULL, Tcl_ForObjCmd, | |
98 | TclCompileForCmd, 1}, | |
99 | {"foreach", (Tcl_CmdProc *) NULL, Tcl_ForeachObjCmd, | |
100 | TclCompileForeachCmd, 1}, | |
101 | {"format", (Tcl_CmdProc *) NULL, Tcl_FormatObjCmd, | |
102 | (CompileProc *) NULL, 1}, | |
103 | {"global", (Tcl_CmdProc *) NULL, Tcl_GlobalObjCmd, | |
104 | (CompileProc *) NULL, 1}, | |
105 | {"if", (Tcl_CmdProc *) NULL, Tcl_IfObjCmd, | |
106 | TclCompileIfCmd, 1}, | |
107 | {"incr", (Tcl_CmdProc *) NULL, Tcl_IncrObjCmd, | |
108 | TclCompileIncrCmd, 1}, | |
109 | {"info", (Tcl_CmdProc *) NULL, Tcl_InfoObjCmd, | |
110 | (CompileProc *) NULL, 1}, | |
111 | {"join", (Tcl_CmdProc *) NULL, Tcl_JoinObjCmd, | |
112 | (CompileProc *) NULL, 1}, | |
113 | {"lappend", (Tcl_CmdProc *) NULL, Tcl_LappendObjCmd, | |
114 | (CompileProc *) NULL, 1}, | |
115 | {"lindex", (Tcl_CmdProc *) NULL, Tcl_LindexObjCmd, | |
116 | (CompileProc *) NULL, 1}, | |
117 | {"linsert", (Tcl_CmdProc *) NULL, Tcl_LinsertObjCmd, | |
118 | (CompileProc *) NULL, 1}, | |
119 | {"list", (Tcl_CmdProc *) NULL, Tcl_ListObjCmd, | |
120 | (CompileProc *) NULL, 1}, | |
121 | {"llength", (Tcl_CmdProc *) NULL, Tcl_LlengthObjCmd, | |
122 | (CompileProc *) NULL, 1}, | |
123 | {"load", (Tcl_CmdProc *) NULL, Tcl_LoadObjCmd, | |
124 | (CompileProc *) NULL, 0}, | |
125 | {"lrange", (Tcl_CmdProc *) NULL, Tcl_LrangeObjCmd, | |
126 | (CompileProc *) NULL, 1}, | |
127 | {"lreplace", (Tcl_CmdProc *) NULL, Tcl_LreplaceObjCmd, | |
128 | (CompileProc *) NULL, 1}, | |
129 | {"lsearch", (Tcl_CmdProc *) NULL, Tcl_LsearchObjCmd, | |
130 | (CompileProc *) NULL, 1}, | |
131 | {"lsort", (Tcl_CmdProc *) NULL, Tcl_LsortObjCmd, | |
132 | (CompileProc *) NULL, 1}, | |
133 | {"namespace", (Tcl_CmdProc *) NULL, Tcl_NamespaceObjCmd, | |
134 | (CompileProc *) NULL, 1}, | |
135 | {"package", (Tcl_CmdProc *) NULL, Tcl_PackageObjCmd, | |
136 | (CompileProc *) NULL, 1}, | |
137 | {"proc", (Tcl_CmdProc *) NULL, Tcl_ProcObjCmd, | |
138 | (CompileProc *) NULL, 1}, | |
139 | {"regexp", (Tcl_CmdProc *) NULL, Tcl_RegexpObjCmd, | |
140 | (CompileProc *) NULL, 1}, | |
141 | {"regsub", (Tcl_CmdProc *) NULL, Tcl_RegsubObjCmd, | |
142 | (CompileProc *) NULL, 1}, | |
143 | {"rename", (Tcl_CmdProc *) NULL, Tcl_RenameObjCmd, | |
144 | (CompileProc *) NULL, 1}, | |
145 | {"return", (Tcl_CmdProc *) NULL, Tcl_ReturnObjCmd, | |
146 | (CompileProc *) NULL, 1}, | |
147 | {"scan", (Tcl_CmdProc *) NULL, Tcl_ScanObjCmd, | |
148 | (CompileProc *) NULL, 1}, | |
149 | {"set", (Tcl_CmdProc *) NULL, Tcl_SetObjCmd, | |
150 | TclCompileSetCmd, 1}, | |
151 | {"split", (Tcl_CmdProc *) NULL, Tcl_SplitObjCmd, | |
152 | (CompileProc *) NULL, 1}, | |
153 | {"string", (Tcl_CmdProc *) NULL, Tcl_StringObjCmd, | |
154 | (CompileProc *) NULL, 1}, | |
155 | {"subst", (Tcl_CmdProc *) NULL, Tcl_SubstObjCmd, | |
156 | (CompileProc *) NULL, 1}, | |
157 | {"switch", (Tcl_CmdProc *) NULL, Tcl_SwitchObjCmd, | |
158 | (CompileProc *) NULL, 1}, | |
159 | {"trace", (Tcl_CmdProc *) NULL, Tcl_TraceObjCmd, | |
160 | (CompileProc *) NULL, 1}, | |
161 | {"unset", (Tcl_CmdProc *) NULL, Tcl_UnsetObjCmd, | |
162 | (CompileProc *) NULL, 1}, | |
163 | {"uplevel", (Tcl_CmdProc *) NULL, Tcl_UplevelObjCmd, | |
164 | (CompileProc *) NULL, 1}, | |
165 | {"upvar", (Tcl_CmdProc *) NULL, Tcl_UpvarObjCmd, | |
166 | (CompileProc *) NULL, 1}, | |
167 | {"variable", (Tcl_CmdProc *) NULL, Tcl_VariableObjCmd, | |
168 | (CompileProc *) NULL, 1}, | |
169 | {"while", (Tcl_CmdProc *) NULL, Tcl_WhileObjCmd, | |
170 | TclCompileWhileCmd, 1}, | |
171 | ||
172 | /* | |
173 | * Commands in the UNIX core: | |
174 | */ | |
175 | ||
176 | #ifndef TCL_GENERIC_ONLY | |
177 | {"after", (Tcl_CmdProc *) NULL, Tcl_AfterObjCmd, | |
178 | (CompileProc *) NULL, 1}, | |
179 | {"cd", (Tcl_CmdProc *) NULL, Tcl_CdObjCmd, | |
180 | (CompileProc *) NULL, 0}, | |
181 | {"close", (Tcl_CmdProc *) NULL, Tcl_CloseObjCmd, | |
182 | (CompileProc *) NULL, 1}, | |
183 | {"eof", (Tcl_CmdProc *) NULL, Tcl_EofObjCmd, | |
184 | (CompileProc *) NULL, 1}, | |
185 | {"fblocked", (Tcl_CmdProc *) NULL, Tcl_FblockedObjCmd, | |
186 | (CompileProc *) NULL, 1}, | |
187 | {"fconfigure", (Tcl_CmdProc *) NULL, Tcl_FconfigureObjCmd, | |
188 | (CompileProc *) NULL, 0}, | |
189 | {"file", (Tcl_CmdProc *) NULL, Tcl_FileObjCmd, | |
190 | (CompileProc *) NULL, 0}, | |
191 | {"flush", (Tcl_CmdProc *) NULL, Tcl_FlushObjCmd, | |
192 | (CompileProc *) NULL, 1}, | |
193 | {"gets", (Tcl_CmdProc *) NULL, Tcl_GetsObjCmd, | |
194 | (CompileProc *) NULL, 1}, | |
195 | {"glob", (Tcl_CmdProc *) NULL, Tcl_GlobObjCmd, | |
196 | (CompileProc *) NULL, 0}, | |
197 | {"open", (Tcl_CmdProc *) NULL, Tcl_OpenObjCmd, | |
198 | (CompileProc *) NULL, 0}, | |
199 | {"pid", (Tcl_CmdProc *) NULL, Tcl_PidObjCmd, | |
200 | (CompileProc *) NULL, 1}, | |
201 | {"puts", (Tcl_CmdProc *) NULL, Tcl_PutsObjCmd, | |
202 | (CompileProc *) NULL, 1}, | |
203 | {"pwd", (Tcl_CmdProc *) NULL, Tcl_PwdObjCmd, | |
204 | (CompileProc *) NULL, 0}, | |
205 | {"read", (Tcl_CmdProc *) NULL, Tcl_ReadObjCmd, | |
206 | (CompileProc *) NULL, 1}, | |
207 | {"seek", (Tcl_CmdProc *) NULL, Tcl_SeekObjCmd, | |
208 | (CompileProc *) NULL, 1}, | |
209 | {"socket", (Tcl_CmdProc *) NULL, Tcl_SocketObjCmd, | |
210 | (CompileProc *) NULL, 0}, | |
211 | {"tell", (Tcl_CmdProc *) NULL, Tcl_TellObjCmd, | |
212 | (CompileProc *) NULL, 1}, | |
213 | {"time", (Tcl_CmdProc *) NULL, Tcl_TimeObjCmd, | |
214 | (CompileProc *) NULL, 1}, | |
215 | {"update", (Tcl_CmdProc *) NULL, Tcl_UpdateObjCmd, | |
216 | (CompileProc *) NULL, 1}, | |
217 | {"vwait", (Tcl_CmdProc *) NULL, Tcl_VwaitObjCmd, | |
218 | (CompileProc *) NULL, 1}, | |
219 | ||
220 | #ifdef MAC_TCL | |
221 | {"beep", (Tcl_CmdProc *) NULL, Tcl_BeepObjCmd, | |
222 | (CompileProc *) NULL, 0}, | |
223 | {"echo", Tcl_EchoCmd, (Tcl_ObjCmdProc *) NULL, | |
224 | (CompileProc *) NULL, 0}, | |
225 | {"ls", (Tcl_CmdProc *) NULL, Tcl_LsObjCmd, | |
226 | (CompileProc *) NULL, 0}, | |
227 | {"resource", (Tcl_CmdProc *) NULL, Tcl_ResourceObjCmd, | |
228 | (CompileProc *) NULL, 1}, | |
229 | {"source", (Tcl_CmdProc *) NULL, Tcl_MacSourceObjCmd, | |
230 | (CompileProc *) NULL, 0}, | |
231 | #else | |
232 | {"exec", (Tcl_CmdProc *) NULL, Tcl_ExecObjCmd, | |
233 | (CompileProc *) NULL, 0}, | |
234 | {"source", (Tcl_CmdProc *) NULL, Tcl_SourceObjCmd, | |
235 | (CompileProc *) NULL, 0}, | |
236 | #endif /* MAC_TCL */ | |
237 | ||
238 | #endif /* TCL_GENERIC_ONLY */ | |
239 | {NULL, (Tcl_CmdProc *) NULL, (Tcl_ObjCmdProc *) NULL, | |
240 | (CompileProc *) NULL, 0} | |
241 | }; | |
242 | ||
243 | ||
244 | /* | |
245 | *---------------------------------------------------------------------- | |
246 | * | |
247 | * Tcl_CreateInterp -- | |
248 | * | |
249 | * Create a new TCL command interpreter. | |
250 | * | |
251 | * Results: | |
252 | * The return value is a token for the interpreter, which may be | |
253 | * used in calls to procedures like Tcl_CreateCmd, Tcl_Eval, or | |
254 | * Tcl_DeleteInterp. | |
255 | * | |
256 | * Side effects: | |
257 | * The command interpreter is initialized with an empty variable | |
258 | * table and the built-in commands. | |
259 | * | |
260 | *---------------------------------------------------------------------- | |
261 | */ | |
262 | ||
263 | Tcl_Interp * | |
264 | Tcl_CreateInterp() | |
265 | { | |
266 | Interp *iPtr; | |
267 | Tcl_Interp *interp; | |
268 | Command *cmdPtr; | |
269 | BuiltinFunc *builtinFuncPtr; | |
270 | MathFunc *mathFuncPtr; | |
271 | Tcl_HashEntry *hPtr; | |
272 | CmdInfo *cmdInfoPtr; | |
273 | int i; | |
274 | union { | |
275 | char c[sizeof(short)]; | |
276 | short s; | |
277 | } order; | |
278 | #ifdef TCL_COMPILE_STATS | |
279 | ByteCodeStats *statsPtr; | |
280 | #endif /* TCL_COMPILE_STATS */ | |
281 | ||
282 | TclInitSubsystems(NULL); | |
283 | ||
284 | /* | |
285 | * Panic if someone updated the CallFrame structure without | |
286 | * also updating the Tcl_CallFrame structure (or vice versa). | |
287 | */ | |
288 | ||
289 | if (sizeof(Tcl_CallFrame) != sizeof(CallFrame)) { | |
290 | /*NOTREACHED*/ | |
291 | panic("Tcl_CallFrame and CallFrame are not the same size"); | |
292 | } | |
293 | ||
294 | /* | |
295 | * Initialize support for namespaces and create the global namespace | |
296 | * (whose name is ""; an alias is "::"). This also initializes the | |
297 | * Tcl object type table and other object management code. | |
298 | */ | |
299 | ||
300 | iPtr = (Interp *) ckalloc(sizeof(Interp)); | |
301 | interp = (Tcl_Interp *) iPtr; | |
302 | ||
303 | iPtr->result = iPtr->resultSpace; | |
304 | iPtr->freeProc = NULL; | |
305 | iPtr->errorLine = 0; | |
306 | iPtr->objResultPtr = Tcl_NewObj(); | |
307 | Tcl_IncrRefCount(iPtr->objResultPtr); | |
308 | iPtr->handle = TclHandleCreate(iPtr); | |
309 | iPtr->globalNsPtr = NULL; | |
310 | iPtr->hiddenCmdTablePtr = NULL; | |
311 | iPtr->interpInfo = NULL; | |
312 | Tcl_InitHashTable(&iPtr->mathFuncTable, TCL_STRING_KEYS); | |
313 | ||
314 | iPtr->numLevels = 0; | |
315 | iPtr->maxNestingDepth = 1000; | |
316 | iPtr->framePtr = NULL; | |
317 | iPtr->varFramePtr = NULL; | |
318 | iPtr->activeTracePtr = NULL; | |
319 | iPtr->returnCode = TCL_OK; | |
320 | iPtr->errorInfo = NULL; | |
321 | iPtr->errorCode = NULL; | |
322 | ||
323 | iPtr->appendResult = NULL; | |
324 | iPtr->appendAvl = 0; | |
325 | iPtr->appendUsed = 0; | |
326 | ||
327 | Tcl_InitHashTable(&iPtr->packageTable, TCL_STRING_KEYS); | |
328 | iPtr->packageUnknown = NULL; | |
329 | iPtr->cmdCount = 0; | |
330 | iPtr->termOffset = 0; | |
331 | TclInitLiteralTable(&(iPtr->literalTable)); | |
332 | iPtr->compileEpoch = 0; | |
333 | iPtr->compiledProcPtr = NULL; | |
334 | iPtr->resolverPtr = NULL; | |
335 | iPtr->evalFlags = 0; | |
336 | iPtr->scriptFile = NULL; | |
337 | iPtr->flags = 0; | |
338 | iPtr->tracePtr = NULL; | |
339 | iPtr->assocData = (Tcl_HashTable *) NULL; | |
340 | iPtr->execEnvPtr = NULL; /* set after namespaces initialized */ | |
341 | iPtr->emptyObjPtr = Tcl_NewObj(); /* another empty object */ | |
342 | Tcl_IncrRefCount(iPtr->emptyObjPtr); | |
343 | iPtr->resultSpace[0] = 0; | |
344 | ||
345 | iPtr->globalNsPtr = NULL; /* force creation of global ns below */ | |
346 | iPtr->globalNsPtr = (Namespace *) Tcl_CreateNamespace(interp, "", | |
347 | (ClientData) NULL, (Tcl_NamespaceDeleteProc *) NULL); | |
348 | if (iPtr->globalNsPtr == NULL) { | |
349 | panic("Tcl_CreateInterp: can't create global namespace"); | |
350 | } | |
351 | ||
352 | /* | |
353 | * Initialize support for code compilation and execution. We call | |
354 | * TclCreateExecEnv after initializing namespaces since it tries to | |
355 | * reference a Tcl variable (it links to the Tcl "tcl_traceExec" | |
356 | * variable). | |
357 | */ | |
358 | ||
359 | iPtr->execEnvPtr = TclCreateExecEnv(interp); | |
360 | ||
361 | /* | |
362 | * Initialize the compilation and execution statistics kept for this | |
363 | * interpreter. | |
364 | */ | |
365 | ||
366 | #ifdef TCL_COMPILE_STATS | |
367 | statsPtr = &(iPtr->stats); | |
368 | statsPtr->numExecutions = 0; | |
369 | statsPtr->numCompilations = 0; | |
370 | statsPtr->numByteCodesFreed = 0; | |
371 | (VOID *) memset(statsPtr->instructionCount, 0, | |
372 | sizeof(statsPtr->instructionCount)); | |
373 | ||
374 | statsPtr->totalSrcBytes = 0.0; | |
375 | statsPtr->totalByteCodeBytes = 0.0; | |
376 | statsPtr->currentSrcBytes = 0.0; | |
377 | statsPtr->currentByteCodeBytes = 0.0; | |
378 | (VOID *) memset(statsPtr->srcCount, 0, sizeof(statsPtr->srcCount)); | |
379 | (VOID *) memset(statsPtr->byteCodeCount, 0, | |
380 | sizeof(statsPtr->byteCodeCount)); | |
381 | (VOID *) memset(statsPtr->lifetimeCount, 0, | |
382 | sizeof(statsPtr->lifetimeCount)); | |
383 | ||
384 | statsPtr->currentInstBytes = 0.0; | |
385 | statsPtr->currentLitBytes = 0.0; | |
386 | statsPtr->currentExceptBytes = 0.0; | |
387 | statsPtr->currentAuxBytes = 0.0; | |
388 | statsPtr->currentCmdMapBytes = 0.0; | |
389 | ||
390 | statsPtr->numLiteralsCreated = 0; | |
391 | statsPtr->totalLitStringBytes = 0.0; | |
392 | statsPtr->currentLitStringBytes = 0.0; | |
393 | (VOID *) memset(statsPtr->literalCount, 0, | |
394 | sizeof(statsPtr->literalCount)); | |
395 | #endif /* TCL_COMPILE_STATS */ | |
396 | ||
397 | /* | |
398 | * Initialise the stub table pointer. | |
399 | */ | |
400 | ||
401 | iPtr->stubTable = &tclStubs; | |
402 | ||
403 | ||
404 | /* | |
405 | * Create the core commands. Do it here, rather than calling | |
406 | * Tcl_CreateCommand, because it's faster (there's no need to check for | |
407 | * a pre-existing command by the same name). If a command has a | |
408 | * Tcl_CmdProc but no Tcl_ObjCmdProc, set the Tcl_ObjCmdProc to | |
409 | * TclInvokeStringCommand. This is an object-based wrapper procedure | |
410 | * that extracts strings, calls the string procedure, and creates an | |
411 | * object for the result. Similarly, if a command has a Tcl_ObjCmdProc | |
412 | * but no Tcl_CmdProc, set the Tcl_CmdProc to TclInvokeObjectCommand. | |
413 | */ | |
414 | ||
415 | for (cmdInfoPtr = builtInCmds; cmdInfoPtr->name != NULL; | |
416 | cmdInfoPtr++) { | |
417 | int new; | |
418 | Tcl_HashEntry *hPtr; | |
419 | ||
420 | if ((cmdInfoPtr->proc == (Tcl_CmdProc *) NULL) | |
421 | && (cmdInfoPtr->objProc == (Tcl_ObjCmdProc *) NULL) | |
422 | && (cmdInfoPtr->compileProc == (CompileProc *) NULL)) { | |
423 | panic("Tcl_CreateInterp: builtin command with NULL string and object command procs and a NULL compile proc\n"); | |
424 | } | |
425 | ||
426 | hPtr = Tcl_CreateHashEntry(&iPtr->globalNsPtr->cmdTable, | |
427 | cmdInfoPtr->name, &new); | |
428 | if (new) { | |
429 | cmdPtr = (Command *) ckalloc(sizeof(Command)); | |
430 | cmdPtr->hPtr = hPtr; | |
431 | cmdPtr->nsPtr = iPtr->globalNsPtr; | |
432 | cmdPtr->refCount = 1; | |
433 | cmdPtr->cmdEpoch = 0; | |
434 | cmdPtr->compileProc = cmdInfoPtr->compileProc; | |
435 | if (cmdInfoPtr->proc == (Tcl_CmdProc *) NULL) { | |
436 | cmdPtr->proc = TclInvokeObjectCommand; | |
437 | cmdPtr->clientData = (ClientData) cmdPtr; | |
438 | } else { | |
439 | cmdPtr->proc = cmdInfoPtr->proc; | |
440 | cmdPtr->clientData = (ClientData) NULL; | |
441 | } | |
442 | if (cmdInfoPtr->objProc == (Tcl_ObjCmdProc *) NULL) { | |
443 | cmdPtr->objProc = TclInvokeStringCommand; | |
444 | cmdPtr->objClientData = (ClientData) cmdPtr; | |
445 | } else { | |
446 | cmdPtr->objProc = cmdInfoPtr->objProc; | |
447 | cmdPtr->objClientData = (ClientData) NULL; | |
448 | } | |
449 | cmdPtr->deleteProc = NULL; | |
450 | cmdPtr->deleteData = (ClientData) NULL; | |
451 | cmdPtr->deleted = 0; | |
452 | cmdPtr->importRefPtr = NULL; | |
453 | Tcl_SetHashValue(hPtr, cmdPtr); | |
454 | } | |
455 | } | |
456 | ||
457 | /* | |
458 | * Register the builtin math functions. | |
459 | */ | |
460 | ||
461 | i = 0; | |
462 | for (builtinFuncPtr = builtinFuncTable; builtinFuncPtr->name != NULL; | |
463 | builtinFuncPtr++) { | |
464 | Tcl_CreateMathFunc((Tcl_Interp *) iPtr, builtinFuncPtr->name, | |
465 | builtinFuncPtr->numArgs, builtinFuncPtr->argTypes, | |
466 | (Tcl_MathProc *) NULL, (ClientData) 0); | |
467 | hPtr = Tcl_FindHashEntry(&iPtr->mathFuncTable, | |
468 | builtinFuncPtr->name); | |
469 | if (hPtr == NULL) { | |
470 | panic("Tcl_CreateInterp: Tcl_CreateMathFunc incorrectly registered '%s'", builtinFuncPtr->name); | |
471 | return NULL; | |
472 | } | |
473 | mathFuncPtr = (MathFunc *) Tcl_GetHashValue(hPtr); | |
474 | mathFuncPtr->builtinFuncIndex = i; | |
475 | i++; | |
476 | } | |
477 | iPtr->flags |= EXPR_INITIALIZED; | |
478 | ||
479 | /* | |
480 | * Do Multiple/Safe Interps Tcl init stuff | |
481 | */ | |
482 | ||
483 | TclInterpInit(interp); | |
484 | ||
485 | /* | |
486 | * We used to create the "errorInfo" and "errorCode" global vars at this | |
487 | * point because so much of the Tcl implementation assumes they already | |
488 | * exist. This is not quite enough, however, since they can be unset | |
489 | * at any time. | |
490 | * | |
491 | * There are 2 choices: | |
492 | * + Check every place where a GetVar of those is used | |
493 | * and the NULL result is not checked (like in tclLoad.c) | |
494 | * + Make SetVar,... NULL friendly | |
495 | * We choose the second option because : | |
496 | * + It is easy and low cost to check for NULL pointer before | |
497 | * calling strlen() | |
498 | * + It can be helpfull to other people using those API | |
499 | * + Passing a NULL value to those closest 'meaning' is empty string | |
500 | * (specially with the new objects where 0 bytes strings are ok) | |
501 | * So the following init is commented out: -- dl | |
502 | * | |
503 | * (void) Tcl_SetVar2((Tcl_Interp *)iPtr, "errorInfo", (char *) NULL, | |
504 | * "", TCL_GLOBAL_ONLY); | |
505 | * (void) Tcl_SetVar2((Tcl_Interp *)iPtr, "errorCode", (char *) NULL, | |
506 | * "NONE", TCL_GLOBAL_ONLY); | |
507 | */ | |
508 | ||
509 | #ifndef TCL_GENERIC_ONLY | |
510 | TclSetupEnv(interp); | |
511 | #endif | |
512 | ||
513 | /* | |
514 | * Compute the byte order of this machine. | |
515 | */ | |
516 | ||
517 | order.s = 1; | |
518 | Tcl_SetVar2(interp, "tcl_platform", "byteOrder", | |
519 | ((order.c[0] == 1) ? "littleEndian" : "bigEndian"), | |
520 | TCL_GLOBAL_ONLY); | |
521 | ||
522 | /* | |
523 | * Set up other variables such as tcl_version and tcl_library | |
524 | */ | |
525 | ||
526 | Tcl_SetVar(interp, "tcl_patchLevel", TCL_PATCH_LEVEL, TCL_GLOBAL_ONLY); | |
527 | Tcl_SetVar(interp, "tcl_version", TCL_VERSION, TCL_GLOBAL_ONLY); | |
528 | Tcl_TraceVar2(interp, "tcl_precision", (char *) NULL, | |
529 | TCL_GLOBAL_ONLY|TCL_TRACE_READS|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | |
530 | TclPrecTraceProc, (ClientData) NULL); | |
531 | TclpSetVariables(interp); | |
532 | ||
533 | #ifdef TCL_THREADS | |
534 | /* | |
535 | * The existence of the "threaded" element of the tcl_platform array indicates | |
536 | * that this particular Tcl shell has been compiled with threads turned on. | |
537 | * Using "info exists tcl_platform(threaded)" a Tcl script can introspect on the | |
538 | * interpreter level of thread safety. | |
539 | */ | |
540 | ||
541 | ||
542 | Tcl_SetVar2(interp, "tcl_platform", "threaded", "1", | |
543 | TCL_GLOBAL_ONLY); | |
544 | #endif | |
545 | ||
546 | /* | |
547 | * Register Tcl's version number. | |
548 | */ | |
549 | ||
550 | Tcl_PkgProvideEx(interp, "Tcl", TCL_VERSION, (ClientData) &tclStubs); | |
551 | ||
552 | #ifdef Tcl_InitStubs | |
553 | #undef Tcl_InitStubs | |
554 | #endif | |
555 | Tcl_InitStubs(interp, TCL_VERSION, 1); | |
556 | ||
557 | return interp; | |
558 | } | |
559 | ||
560 | /* | |
561 | *---------------------------------------------------------------------- | |
562 | * | |
563 | * TclHideUnsafeCommands -- | |
564 | * | |
565 | * Hides base commands that are not marked as safe from this | |
566 | * interpreter. | |
567 | * | |
568 | * Results: | |
569 | * TCL_OK if it succeeds, TCL_ERROR else. | |
570 | * | |
571 | * Side effects: | |
572 | * Hides functionality in an interpreter. | |
573 | * | |
574 | *---------------------------------------------------------------------- | |
575 | */ | |
576 | ||
577 | int | |
578 | TclHideUnsafeCommands(interp) | |
579 | Tcl_Interp *interp; /* Hide commands in this interpreter. */ | |
580 | { | |
581 | register CmdInfo *cmdInfoPtr; | |
582 | ||
583 | if (interp == (Tcl_Interp *) NULL) { | |
584 | return TCL_ERROR; | |
585 | } | |
586 | for (cmdInfoPtr = builtInCmds; cmdInfoPtr->name != NULL; cmdInfoPtr++) { | |
587 | if (!cmdInfoPtr->isSafe) { | |
588 | Tcl_HideCommand(interp, cmdInfoPtr->name, cmdInfoPtr->name); | |
589 | } | |
590 | } | |
591 | return TCL_OK; | |
592 | } | |
593 | ||
594 | /* | |
595 | *-------------------------------------------------------------- | |
596 | * | |
597 | * Tcl_CallWhenDeleted -- | |
598 | * | |
599 | * Arrange for a procedure to be called before a given | |
600 | * interpreter is deleted. The procedure is called as soon | |
601 | * as Tcl_DeleteInterp is called; if Tcl_CallWhenDeleted is | |
602 | * called on an interpreter that has already been deleted, | |
603 | * the procedure will be called when the last Tcl_Release is | |
604 | * done on the interpreter. | |
605 | * | |
606 | * Results: | |
607 | * None. | |
608 | * | |
609 | * Side effects: | |
610 | * When Tcl_DeleteInterp is invoked to delete interp, | |
611 | * proc will be invoked. See the manual entry for | |
612 | * details. | |
613 | * | |
614 | *-------------------------------------------------------------- | |
615 | */ | |
616 | ||
617 | void | |
618 | Tcl_CallWhenDeleted(interp, proc, clientData) | |
619 | Tcl_Interp *interp; /* Interpreter to watch. */ | |
620 | Tcl_InterpDeleteProc *proc; /* Procedure to call when interpreter | |
621 | * is about to be deleted. */ | |
622 | ClientData clientData; /* One-word value to pass to proc. */ | |
623 | { | |
624 | Interp *iPtr = (Interp *) interp; | |
625 | static int assocDataCounter = 0; | |
626 | #ifdef TCL_THREADS | |
627 | static Tcl_Mutex assocMutex; | |
628 | #endif | |
629 | int new; | |
630 | char buffer[32 + TCL_INTEGER_SPACE]; | |
631 | AssocData *dPtr = (AssocData *) ckalloc(sizeof(AssocData)); | |
632 | Tcl_HashEntry *hPtr; | |
633 | ||
634 | Tcl_MutexLock(&assocMutex); | |
635 | sprintf(buffer, "Assoc Data Key #%d", assocDataCounter); | |
636 | assocDataCounter++; | |
637 | Tcl_MutexUnlock(&assocMutex); | |
638 | ||
639 | if (iPtr->assocData == (Tcl_HashTable *) NULL) { | |
640 | iPtr->assocData = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable)); | |
641 | Tcl_InitHashTable(iPtr->assocData, TCL_STRING_KEYS); | |
642 | } | |
643 | hPtr = Tcl_CreateHashEntry(iPtr->assocData, buffer, &new); | |
644 | dPtr->proc = proc; | |
645 | dPtr->clientData = clientData; | |
646 | Tcl_SetHashValue(hPtr, dPtr); | |
647 | } | |
648 | ||
649 | /* | |
650 | *-------------------------------------------------------------- | |
651 | * | |
652 | * Tcl_DontCallWhenDeleted -- | |
653 | * | |
654 | * Cancel the arrangement for a procedure to be called when | |
655 | * a given interpreter is deleted. | |
656 | * | |
657 | * Results: | |
658 | * None. | |
659 | * | |
660 | * Side effects: | |
661 | * If proc and clientData were previously registered as a | |
662 | * callback via Tcl_CallWhenDeleted, they are unregistered. | |
663 | * If they weren't previously registered then nothing | |
664 | * happens. | |
665 | * | |
666 | *-------------------------------------------------------------- | |
667 | */ | |
668 | ||
669 | void | |
670 | Tcl_DontCallWhenDeleted(interp, proc, clientData) | |
671 | Tcl_Interp *interp; /* Interpreter to watch. */ | |
672 | Tcl_InterpDeleteProc *proc; /* Procedure to call when interpreter | |
673 | * is about to be deleted. */ | |
674 | ClientData clientData; /* One-word value to pass to proc. */ | |
675 | { | |
676 | Interp *iPtr = (Interp *) interp; | |
677 | Tcl_HashTable *hTablePtr; | |
678 | Tcl_HashSearch hSearch; | |
679 | Tcl_HashEntry *hPtr; | |
680 | AssocData *dPtr; | |
681 | ||
682 | hTablePtr = iPtr->assocData; | |
683 | if (hTablePtr == (Tcl_HashTable *) NULL) { | |
684 | return; | |
685 | } | |
686 | for (hPtr = Tcl_FirstHashEntry(hTablePtr, &hSearch); hPtr != NULL; | |
687 | hPtr = Tcl_NextHashEntry(&hSearch)) { | |
688 | dPtr = (AssocData *) Tcl_GetHashValue(hPtr); | |
689 | if ((dPtr->proc == proc) && (dPtr->clientData == clientData)) { | |
690 | ckfree((char *) dPtr); | |
691 | Tcl_DeleteHashEntry(hPtr); | |
692 | return; | |
693 | } | |
694 | } | |
695 | } | |
696 | ||
697 | /* | |
698 | *---------------------------------------------------------------------- | |
699 | * | |
700 | * Tcl_SetAssocData -- | |
701 | * | |
702 | * Creates a named association between user-specified data, a delete | |
703 | * function and this interpreter. If the association already exists | |
704 | * the data is overwritten with the new data. The delete function will | |
705 | * be invoked when the interpreter is deleted. | |
706 | * | |
707 | * Results: | |
708 | * None. | |
709 | * | |
710 | * Side effects: | |
711 | * Sets the associated data, creates the association if needed. | |
712 | * | |
713 | *---------------------------------------------------------------------- | |
714 | */ | |
715 | ||
716 | void | |
717 | Tcl_SetAssocData(interp, name, proc, clientData) | |
718 | Tcl_Interp *interp; /* Interpreter to associate with. */ | |
719 | char *name; /* Name for association. */ | |
720 | Tcl_InterpDeleteProc *proc; /* Proc to call when interpreter is | |
721 | * about to be deleted. */ | |
722 | ClientData clientData; /* One-word value to pass to proc. */ | |
723 | { | |
724 | Interp *iPtr = (Interp *) interp; | |
725 | AssocData *dPtr; | |
726 | Tcl_HashEntry *hPtr; | |
727 | int new; | |
728 | ||
729 | if (iPtr->assocData == (Tcl_HashTable *) NULL) { | |
730 | iPtr->assocData = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable)); | |
731 | Tcl_InitHashTable(iPtr->assocData, TCL_STRING_KEYS); | |
732 | } | |
733 | hPtr = Tcl_CreateHashEntry(iPtr->assocData, name, &new); | |
734 | if (new == 0) { | |
735 | dPtr = (AssocData *) Tcl_GetHashValue(hPtr); | |
736 | } else { | |
737 | dPtr = (AssocData *) ckalloc(sizeof(AssocData)); | |
738 | } | |
739 | dPtr->proc = proc; | |
740 | dPtr->clientData = clientData; | |
741 | ||
742 | Tcl_SetHashValue(hPtr, dPtr); | |
743 | } | |
744 | ||
745 | /* | |
746 | *---------------------------------------------------------------------- | |
747 | * | |
748 | * Tcl_DeleteAssocData -- | |
749 | * | |
750 | * Deletes a named association of user-specified data with | |
751 | * the specified interpreter. | |
752 | * | |
753 | * Results: | |
754 | * None. | |
755 | * | |
756 | * Side effects: | |
757 | * Deletes the association. | |
758 | * | |
759 | *---------------------------------------------------------------------- | |
760 | */ | |
761 | ||
762 | void | |
763 | Tcl_DeleteAssocData(interp, name) | |
764 | Tcl_Interp *interp; /* Interpreter to associate with. */ | |
765 | char *name; /* Name of association. */ | |
766 | { | |
767 | Interp *iPtr = (Interp *) interp; | |
768 | AssocData *dPtr; | |
769 | Tcl_HashEntry *hPtr; | |
770 | ||
771 | if (iPtr->assocData == (Tcl_HashTable *) NULL) { | |
772 | return; | |
773 | } | |
774 | hPtr = Tcl_FindHashEntry(iPtr->assocData, name); | |
775 | if (hPtr == (Tcl_HashEntry *) NULL) { | |
776 | return; | |
777 | } | |
778 | dPtr = (AssocData *) Tcl_GetHashValue(hPtr); | |
779 | if (dPtr->proc != NULL) { | |
780 | (dPtr->proc) (dPtr->clientData, interp); | |
781 | } | |
782 | ckfree((char *) dPtr); | |
783 | Tcl_DeleteHashEntry(hPtr); | |
784 | } | |
785 | ||
786 | /* | |
787 | *---------------------------------------------------------------------- | |
788 | * | |
789 | * Tcl_GetAssocData -- | |
790 | * | |
791 | * Returns the client data associated with this name in the | |
792 | * specified interpreter. | |
793 | * | |
794 | * Results: | |
795 | * The client data in the AssocData record denoted by the named | |
796 | * association, or NULL. | |
797 | * | |
798 | * Side effects: | |
799 | * None. | |
800 | * | |
801 | *---------------------------------------------------------------------- | |
802 | */ | |
803 | ||
804 | ClientData | |
805 | Tcl_GetAssocData(interp, name, procPtr) | |
806 | Tcl_Interp *interp; /* Interpreter associated with. */ | |
807 | char *name; /* Name of association. */ | |
808 | Tcl_InterpDeleteProc **procPtr; /* Pointer to place to store address | |
809 | * of current deletion callback. */ | |
810 | { | |
811 | Interp *iPtr = (Interp *) interp; | |
812 | AssocData *dPtr; | |
813 | Tcl_HashEntry *hPtr; | |
814 | ||
815 | if (iPtr->assocData == (Tcl_HashTable *) NULL) { | |
816 | return (ClientData) NULL; | |
817 | } | |
818 | hPtr = Tcl_FindHashEntry(iPtr->assocData, name); | |
819 | if (hPtr == (Tcl_HashEntry *) NULL) { | |
820 | return (ClientData) NULL; | |
821 | } | |
822 | dPtr = (AssocData *) Tcl_GetHashValue(hPtr); | |
823 | if (procPtr != (Tcl_InterpDeleteProc **) NULL) { | |
824 | *procPtr = dPtr->proc; | |
825 | } | |
826 | return dPtr->clientData; | |
827 | } | |
828 | ||
829 | /* | |
830 | *---------------------------------------------------------------------- | |
831 | * | |
832 | * Tcl_InterpDeleted -- | |
833 | * | |
834 | * Returns nonzero if the interpreter has been deleted with a call | |
835 | * to Tcl_DeleteInterp. | |
836 | * | |
837 | * Results: | |
838 | * Nonzero if the interpreter is deleted, zero otherwise. | |
839 | * | |
840 | * Side effects: | |
841 | * None. | |
842 | * | |
843 | *---------------------------------------------------------------------- | |
844 | */ | |
845 | ||
846 | int | |
847 | Tcl_InterpDeleted(interp) | |
848 | Tcl_Interp *interp; | |
849 | { | |
850 | return (((Interp *) interp)->flags & DELETED) ? 1 : 0; | |
851 | } | |
852 | ||
853 | /* | |
854 | *---------------------------------------------------------------------- | |
855 | * | |
856 | * Tcl_DeleteInterp -- | |
857 | * | |
858 | * Ensures that the interpreter will be deleted eventually. If there | |
859 | * are no Tcl_Preserve calls in effect for this interpreter, it is | |
860 | * deleted immediately, otherwise the interpreter is deleted when | |
861 | * the last Tcl_Preserve is matched by a call to Tcl_Release. In either | |
862 | * case, the procedure runs the currently registered deletion callbacks. | |
863 | * | |
864 | * Results: | |
865 | * None. | |
866 | * | |
867 | * Side effects: | |
868 | * The interpreter is marked as deleted. The caller may still use it | |
869 | * safely if there are calls to Tcl_Preserve in effect for the | |
870 | * interpreter, but further calls to Tcl_Eval etc in this interpreter | |
871 | * will fail. | |
872 | * | |
873 | *---------------------------------------------------------------------- | |
874 | */ | |
875 | ||
876 | void | |
877 | Tcl_DeleteInterp(interp) | |
878 | Tcl_Interp *interp; /* Token for command interpreter (returned | |
879 | * by a previous call to Tcl_CreateInterp). */ | |
880 | { | |
881 | Interp *iPtr = (Interp *) interp; | |
882 | ||
883 | /* | |
884 | * If the interpreter has already been marked deleted, just punt. | |
885 | */ | |
886 | ||
887 | if (iPtr->flags & DELETED) { | |
888 | return; | |
889 | } | |
890 | ||
891 | /* | |
892 | * Mark the interpreter as deleted. No further evals will be allowed. | |
893 | */ | |
894 | ||
895 | iPtr->flags |= DELETED; | |
896 | ||
897 | /* | |
898 | * Ensure that the interpreter is eventually deleted. | |
899 | */ | |
900 | ||
901 | Tcl_EventuallyFree((ClientData) interp, | |
902 | (Tcl_FreeProc *) DeleteInterpProc); | |
903 | } | |
904 | ||
905 | /* | |
906 | *---------------------------------------------------------------------- | |
907 | * | |
908 | * DeleteInterpProc -- | |
909 | * | |
910 | * Helper procedure to delete an interpreter. This procedure is | |
911 | * called when the last call to Tcl_Preserve on this interpreter | |
912 | * is matched by a call to Tcl_Release. The procedure cleans up | |
913 | * all resources used in the interpreter and calls all currently | |
914 | * registered interpreter deletion callbacks. | |
915 | * | |
916 | * Results: | |
917 | * None. | |
918 | * | |
919 | * Side effects: | |
920 | * Whatever the interpreter deletion callbacks do. Frees resources | |
921 | * used by the interpreter. | |
922 | * | |
923 | *---------------------------------------------------------------------- | |
924 | */ | |
925 | ||
926 | static void | |
927 | DeleteInterpProc(interp) | |
928 | Tcl_Interp *interp; /* Interpreter to delete. */ | |
929 | { | |
930 | Interp *iPtr = (Interp *) interp; | |
931 | Tcl_HashEntry *hPtr; | |
932 | Tcl_HashSearch search; | |
933 | Tcl_HashTable *hTablePtr; | |
934 | ResolverScheme *resPtr, *nextResPtr; | |
935 | ||
936 | /* | |
937 | * Punt if there is an error in the Tcl_Release/Tcl_Preserve matchup. | |
938 | */ | |
939 | ||
940 | if (iPtr->numLevels > 0) { | |
941 | panic("DeleteInterpProc called with active evals"); | |
942 | } | |
943 | ||
944 | /* | |
945 | * The interpreter should already be marked deleted; otherwise how | |
946 | * did we get here? | |
947 | */ | |
948 | ||
949 | if (!(iPtr->flags & DELETED)) { | |
950 | panic("DeleteInterpProc called on interpreter not marked deleted"); | |
951 | } | |
952 | ||
953 | TclHandleFree(iPtr->handle); | |
954 | ||
955 | /* | |
956 | * Dismantle everything in the global namespace except for the | |
957 | * "errorInfo" and "errorCode" variables. These remain until the | |
958 | * namespace is actually destroyed, in case any errors occur. | |
959 | * | |
960 | * Dismantle the namespace here, before we clear the assocData. If any | |
961 | * background errors occur here, they will be deleted below. | |
962 | */ | |
963 | ||
964 | TclTeardownNamespace(iPtr->globalNsPtr); | |
965 | ||
966 | /* | |
967 | * Delete all the hidden commands. | |
968 | */ | |
969 | ||
970 | hTablePtr = iPtr->hiddenCmdTablePtr; | |
971 | if (hTablePtr != NULL) { | |
972 | /* | |
973 | * Non-pernicious deletion. The deletion callbacks will not be | |
974 | * allowed to create any new hidden or non-hidden commands. | |
975 | * Tcl_DeleteCommandFromToken() will remove the entry from the | |
976 | * hiddenCmdTablePtr. | |
977 | */ | |
978 | ||
979 | hPtr = Tcl_FirstHashEntry(hTablePtr, &search); | |
980 | for ( ; hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { | |
981 | Tcl_DeleteCommandFromToken(interp, | |
982 | (Tcl_Command) Tcl_GetHashValue(hPtr)); | |
983 | } | |
984 | Tcl_DeleteHashTable(hTablePtr); | |
985 | ckfree((char *) hTablePtr); | |
986 | } | |
987 | /* | |
988 | * Tear down the math function table. | |
989 | */ | |
990 | ||
991 | for (hPtr = Tcl_FirstHashEntry(&iPtr->mathFuncTable, &search); | |
992 | hPtr != NULL; | |
993 | hPtr = Tcl_NextHashEntry(&search)) { | |
994 | ckfree((char *) Tcl_GetHashValue(hPtr)); | |
995 | } | |
996 | Tcl_DeleteHashTable(&iPtr->mathFuncTable); | |
997 | ||
998 | /* | |
999 | * Invoke deletion callbacks; note that a callback can create new | |
1000 | * callbacks, so we iterate. | |
1001 | */ | |
1002 | ||
1003 | while (iPtr->assocData != (Tcl_HashTable *) NULL) { | |
1004 | AssocData *dPtr; | |
1005 | ||
1006 | hTablePtr = iPtr->assocData; | |
1007 | iPtr->assocData = (Tcl_HashTable *) NULL; | |
1008 | for (hPtr = Tcl_FirstHashEntry(hTablePtr, &search); | |
1009 | hPtr != NULL; | |
1010 | hPtr = Tcl_FirstHashEntry(hTablePtr, &search)) { | |
1011 | dPtr = (AssocData *) Tcl_GetHashValue(hPtr); | |
1012 | Tcl_DeleteHashEntry(hPtr); | |
1013 | if (dPtr->proc != NULL) { | |
1014 | (*dPtr->proc)(dPtr->clientData, interp); | |
1015 | } | |
1016 | ckfree((char *) dPtr); | |
1017 | } | |
1018 | Tcl_DeleteHashTable(hTablePtr); | |
1019 | ckfree((char *) hTablePtr); | |
1020 | } | |
1021 | ||
1022 | /* | |
1023 | * Finish deleting the global namespace. | |
1024 | */ | |
1025 | ||
1026 | Tcl_DeleteNamespace((Tcl_Namespace *) iPtr->globalNsPtr); | |
1027 | ||
1028 | /* | |
1029 | * Free up the result *after* deleting variables, since variable | |
1030 | * deletion could have transferred ownership of the result string | |
1031 | * to Tcl. | |
1032 | */ | |
1033 | ||
1034 | Tcl_FreeResult(interp); | |
1035 | interp->result = NULL; | |
1036 | Tcl_DecrRefCount(iPtr->objResultPtr); | |
1037 | iPtr->objResultPtr = NULL; | |
1038 | if (iPtr->errorInfo != NULL) { | |
1039 | ckfree(iPtr->errorInfo); | |
1040 | iPtr->errorInfo = NULL; | |
1041 | } | |
1042 | if (iPtr->errorCode != NULL) { | |
1043 | ckfree(iPtr->errorCode); | |
1044 | iPtr->errorCode = NULL; | |
1045 | } | |
1046 | if (iPtr->appendResult != NULL) { | |
1047 | ckfree(iPtr->appendResult); | |
1048 | iPtr->appendResult = NULL; | |
1049 | } | |
1050 | TclFreePackageInfo(iPtr); | |
1051 | while (iPtr->tracePtr != NULL) { | |
1052 | Trace *nextPtr = iPtr->tracePtr->nextPtr; | |
1053 | ||
1054 | ckfree((char *) iPtr->tracePtr); | |
1055 | iPtr->tracePtr = nextPtr; | |
1056 | } | |
1057 | if (iPtr->execEnvPtr != NULL) { | |
1058 | TclDeleteExecEnv(iPtr->execEnvPtr); | |
1059 | } | |
1060 | Tcl_DecrRefCount(iPtr->emptyObjPtr); | |
1061 | iPtr->emptyObjPtr = NULL; | |
1062 | ||
1063 | resPtr = iPtr->resolverPtr; | |
1064 | while (resPtr) { | |
1065 | nextResPtr = resPtr->nextPtr; | |
1066 | ckfree(resPtr->name); | |
1067 | ckfree((char *) resPtr); | |
1068 | resPtr = nextResPtr; | |
1069 | } | |
1070 | ||
1071 | /* | |
1072 | * Free up literal objects created for scripts compiled by the | |
1073 | * interpreter. | |
1074 | */ | |
1075 | ||
1076 | TclDeleteLiteralTable(interp, &(iPtr->literalTable)); | |
1077 | ckfree((char *) iPtr); | |
1078 | } | |
1079 | ||
1080 | /* | |
1081 | *--------------------------------------------------------------------------- | |
1082 | * | |
1083 | * Tcl_HideCommand -- | |
1084 | * | |
1085 | * Makes a command hidden so that it cannot be invoked from within | |
1086 | * an interpreter, only from within an ancestor. | |
1087 | * | |
1088 | * Results: | |
1089 | * A standard Tcl result; also leaves a message in the interp's result | |
1090 | * if an error occurs. | |
1091 | * | |
1092 | * Side effects: | |
1093 | * Removes a command from the command table and create an entry | |
1094 | * into the hidden command table under the specified token name. | |
1095 | * | |
1096 | *--------------------------------------------------------------------------- | |
1097 | */ | |
1098 | ||
1099 | int | |
1100 | Tcl_HideCommand(interp, cmdName, hiddenCmdToken) | |
1101 | Tcl_Interp *interp; /* Interpreter in which to hide command. */ | |
1102 | char *cmdName; /* Name of command to hide. */ | |
1103 | char *hiddenCmdToken; /* Token name of the to-be-hidden command. */ | |
1104 | { | |
1105 | Interp *iPtr = (Interp *) interp; | |
1106 | Tcl_Command cmd; | |
1107 | Command *cmdPtr; | |
1108 | Tcl_HashTable *hiddenCmdTablePtr; | |
1109 | Tcl_HashEntry *hPtr; | |
1110 | int new; | |
1111 | ||
1112 | if (iPtr->flags & DELETED) { | |
1113 | ||
1114 | /* | |
1115 | * The interpreter is being deleted. Do not create any new | |
1116 | * structures, because it is not safe to modify the interpreter. | |
1117 | */ | |
1118 | ||
1119 | return TCL_ERROR; | |
1120 | } | |
1121 | ||
1122 | /* | |
1123 | * Disallow hiding of commands that are currently in a namespace or | |
1124 | * renaming (as part of hiding) into a namespace. | |
1125 | * | |
1126 | * (because the current implementation with a single global table | |
1127 | * and the needed uniqueness of names cause problems with namespaces) | |
1128 | * | |
1129 | * we don't need to check for "::" in cmdName because the real check is | |
1130 | * on the nsPtr below. | |
1131 | * | |
1132 | * hiddenCmdToken is just a string which is not interpreted in any way. | |
1133 | * It may contain :: but the string is not interpreted as a namespace | |
1134 | * qualifier command name. Thus, hiding foo::bar to foo::bar and then | |
1135 | * trying to expose or invoke ::foo::bar will NOT work; but if the | |
1136 | * application always uses the same strings it will get consistent | |
1137 | * behaviour. | |
1138 | * | |
1139 | * But as we currently limit ourselves to the global namespace only | |
1140 | * for the source, in order to avoid potential confusion, | |
1141 | * lets prevent "::" in the token too. --dl | |
1142 | */ | |
1143 |