summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin B Kenny <kennykb@acm.org>2017-03-14 22:13:15 (GMT)
committerKevin B Kenny <kennykb@acm.org>2017-03-14 22:13:15 (GMT)
commitcd42f659661ae1b0035564950db6299334f612da (patch)
tree5af537edbddd86f79239fbcf3b27a4139ef1212e
parente7d7d95f9f17dbfe2200ba4983280d293bff4ba2 (diff)
parente3a6045c86a6fdc2368ab8ebb8b1eb4f55df3bf1 (diff)
downloadtcl-cd42f659661ae1b0035564950db6299334f612da.zip
tcl-cd42f659661ae1b0035564950db6299334f612da.tar.gz
tcl-cd42f659661ae1b0035564950db6299334f612da.tar.bz2
Make 'clock' and 'encoding' into compilable ensembles that play with safe interps
-rw-r--r--generic/tclBasic.c11
-rw-r--r--generic/tclClock.c18
-rw-r--r--generic/tclCmdAH.c406
-rw-r--r--generic/tclInt.h6
-rw-r--r--library/init.tcl11
-rw-r--r--tests/cmdAH.test4
-rw-r--r--tests/interp.test2
7 files changed, 374 insertions, 84 deletions
diff --git a/generic/tclBasic.c b/generic/tclBasic.c
index d6a460d..4bddbce 100644
--- a/generic/tclBasic.c
+++ b/generic/tclBasic.c
@@ -265,7 +265,6 @@ static const CmdInfo builtInCmds[] = {
{"cd", Tcl_CdObjCmd, NULL, NULL, 0},
{"close", Tcl_CloseObjCmd, NULL, NULL, CMD_IS_SAFE},
{"eof", Tcl_EofObjCmd, NULL, NULL, CMD_IS_SAFE},
- {"encoding", Tcl_EncodingObjCmd, NULL, NULL, 0},
{"exec", Tcl_ExecObjCmd, NULL, NULL, 0},
{"exit", Tcl_ExitObjCmd, NULL, NULL, 0},
{"fblocked", Tcl_FblockedObjCmd, NULL, NULL, CMD_IS_SAFE},
@@ -789,16 +788,17 @@ Tcl_CreateInterp(void)
}
/*
- * Create the "array", "binary", "chan", "dict", "file", "info",
- * "namespace" and "string" ensembles. Note that all these commands (and
- * their subcommands that are not present in the global namespace) are
- * wholly safe *except* for "file".
+ * Create the "array", "binary", "chan", "clock", "dict", "encoding",
+ * "file", "info", "namespace" and "string" ensembles. Note that all these
+ * commands (and their subcommands that are not present in the global
+ * namespace) are wholly safe *except* for "clock", "encoding" and "file".
*/
TclInitArrayCmd(interp);
TclInitBinaryCmd(interp);
TclInitChanCmd(interp);
TclInitDictCmd(interp);
+ TclInitEncodingCmd(interp);
TclInitFileCmd(interp);
TclInitInfoCmd(interp);
TclInitNamespaceCmd(interp);
@@ -1026,6 +1026,7 @@ TclHideUnsafeCommands(
Tcl_HideCommand(interp, cmdInfoPtr->name, cmdInfoPtr->name);
}
}
+ TclMakeEncodingCommandSafe(interp); /* Ugh! */
TclMakeFileCommandSafe(interp); /* Ugh! */
return TCL_OK;
}
diff --git a/generic/tclClock.c b/generic/tclClock.c
index c3b29e9..bb9fbeb 100644
--- a/generic/tclClock.c
+++ b/generic/tclClock.c
@@ -253,6 +253,19 @@ TclClockInit(
ClockClientData *data;
int i;
+ /* Structure of the 'clock' ensemble */
+
+ static const EnsembleImplMap clockImplMap[] = {
+ {"add", NULL, TclCompileBasicMin1ArgCmd, NULL, NULL, 0},
+ {"clicks", NULL, TclCompileBasicMin0ArgCmd, NULL, NULL, 0},
+ {"format", NULL, TclCompileBasicMin1ArgCmd, NULL, NULL, 0},
+ {"microseconds", NULL, TclCompileBasicMin0ArgCmd, NULL, NULL, 0},
+ {"milliseconds", NULL, TclCompileBasicMin0ArgCmd, NULL, NULL, 0},
+ {"scan", NULL, TclCompileBasicMin1ArgCmd, NULL, NULL, 0},
+ {"seconds", NULL, TclCompileBasicMin0ArgCmd, NULL, NULL, 0},
+ {NULL, NULL, NULL, NULL, NULL, 0}
+ };
+
/*
* Safe interps get [::clock] as alias to a master, so do not need their
* own copies of the support routines.
@@ -276,6 +289,7 @@ TclClockInit(
/*
* Install the commands.
+ * TODO - Let Tcl_MakeEnsemble do this?
*/
#define TCL_CLOCK_PREFIX_LEN 14 /* == strlen("::tcl::clock::") */
@@ -286,6 +300,10 @@ TclClockInit(
Tcl_CreateObjCommand(interp, cmdName, clockCmdPtr->objCmdProc, data,
ClockDeleteCmdProc);
}
+
+ /* Make the clock ensemble */
+
+ TclMakeEnsemble(interp, "clock", clockImplMap);
}
/*
diff --git a/generic/tclCmdAH.c b/generic/tclCmdAH.c
index 4c299f8..6d66a32 100644
--- a/generic/tclCmdAH.c
+++ b/generic/tclCmdAH.c
@@ -46,9 +46,24 @@ struct ForeachState {
static int CheckAccess(Tcl_Interp *interp, Tcl_Obj *pathPtr,
int mode);
+static int BadEncodingSubcommand(ClientData dummy,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *const objv[]);
+static int EncodingConvertfromObjCmd(ClientData dummy,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *const objv[]);
+static int EncodingConverttoObjCmd(ClientData dummy,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *const objv[]);
static int EncodingDirsObjCmd(ClientData dummy,
Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[]);
+static int EncodingNamesObjCmd(ClientData dummy,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *const objv[]);
+static int EncodingSystemObjCmd(ClientData dummy,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *const objv[]);
static inline int ForeachAssignments(Tcl_Interp *interp,
struct ForeachState *statePtr);
static inline void ForeachCleanup(Tcl_Interp *interp,
@@ -541,79 +556,280 @@ Tcl_EncodingObjCmd(
switch ((enum options) index) {
case ENC_CONVERTTO:
- case ENC_CONVERTFROM: {
- Tcl_Obj *data;
- Tcl_DString ds;
- Tcl_Encoding encoding;
- int length;
- const char *stringPtr;
-
- if (objc == 3) {
- encoding = Tcl_GetEncoding(interp, NULL);
- data = objv[2];
- } else if (objc == 4) {
- if (Tcl_GetEncodingFromObj(interp, objv[2], &encoding) != TCL_OK) {
- return TCL_ERROR;
- }
- data = objv[3];
- } else {
- Tcl_WrongNumArgs(interp, 2, objv, "?encoding? data");
- return TCL_ERROR;
- }
+ return EncodingConverttoObjCmd(dummy, interp, objc, objv);
+ case ENC_CONVERTFROM:
+ return EncodingConvertfromObjCmd(dummy, interp, objc, objv);
+ case ENC_DIRS:
+ return EncodingDirsObjCmd(dummy, interp, objc, objv);
+ case ENC_NAMES:
+ return EncodingNamesObjCmd(dummy, interp, objc, objv);
+ case ENC_SYSTEM:
+ return EncodingSystemObjCmd(dummy, interp, objc, objv);
+ }
+ return TCL_OK;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * TclInitEncodingCmd --
+ *
+ * This function creates the 'encoding' ensemble.
+ *
+ * Results:
+ * Returns the Tcl_Command so created.
+ *
+ * Side effects:
+ * The ensemble is initialized.
+ *
+ * This command is hidden in a safe interpreter.
+ */
- if ((enum options) index == ENC_CONVERTFROM) {
- /*
- * Treat the string as binary data.
- */
+Tcl_Command
+TclInitEncodingCmd(
+ Tcl_Interp* interp) /* Tcl interpreter */
+{
+ static const EnsembleImplMap encodingImplMap[] = {
+ {"convertfrom", EncodingConvertfromObjCmd, TclCompileBasic1Or2ArgCmd, NULL, NULL, 0},
+ {"convertto", EncodingConverttoObjCmd, TclCompileBasic1Or2ArgCmd, NULL, NULL, 0},
+ {"dirs", EncodingDirsObjCmd, TclCompileBasic0Or1ArgCmd, NULL, NULL, 0},
+ {"names", EncodingNamesObjCmd, TclCompileBasic0ArgCmd, NULL, NULL, 0},
+ {"system", EncodingSystemObjCmd, TclCompileBasic0Or1ArgCmd, NULL, NULL, 0},
+ {NULL, NULL, NULL, NULL, NULL, 0}
+ };
+
+ return TclMakeEnsemble(interp, "encoding", encodingImplMap);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * TclMakeEncodingCommandSafe --
+ *
+ * This function hides the unsafe 'dirs' and 'system' subcommands of
+ * the "encoding" Tcl command ensemble. It must be called only from
+ * TclHideUnsafeCommands.
+ *
+ * Results:
+ * A standard Tcl result
+ *
+ * Side effects:
+ * Adds commands to the table of hidden commands.
+ *
+ *-----------------------------------------------------------------------------
+ */
- stringPtr = (char *) Tcl_GetByteArrayFromObj(data, &length);
- Tcl_ExternalToUtfDString(encoding, stringPtr, length, &ds);
+int
+TclMakeEncodingCommandSafe(
+ Tcl_Interp* interp) /* Tcl interpreter */
+{
+ static const struct {
+ const char *cmdName;
+ int unsafe;
+ } unsafeInfo[] = {
+ {"convertfrom", 0},
+ {"convertto", 0},
+ {"dirs", 1},
+ {"names", 0},
+ {"system", 0},
+ {NULL, 0}
+ };
- /*
- * Note that we cannot use Tcl_DStringResult here because it will
- * truncate the string at the first null byte.
- */
+ int i;
+ Tcl_DString oldBuf, newBuf;
- Tcl_SetObjResult(interp, TclDStringToObj(&ds));
- } else {
- /*
- * Store the result as binary data.
- */
-
- stringPtr = TclGetStringFromObj(data, &length);
- Tcl_UtfToExternalDString(encoding, stringPtr, length, &ds);
- Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(
- (unsigned char *) Tcl_DStringValue(&ds),
- Tcl_DStringLength(&ds)));
- Tcl_DStringFree(&ds);
+ Tcl_DStringInit(&oldBuf);
+ TclDStringAppendLiteral(&oldBuf, "::tcl::encoding::");
+ Tcl_DStringInit(&newBuf);
+ TclDStringAppendLiteral(&newBuf, "tcl:encoding:");
+ for (i=0 ; unsafeInfo[i].cmdName != NULL ; i++) {
+ if (unsafeInfo[i].unsafe) {
+ const char *oldName, *newName;
+
+ Tcl_DStringSetLength(&oldBuf, 17);
+ oldName = Tcl_DStringAppend(&oldBuf, unsafeInfo[i].cmdName, -1);
+ Tcl_DStringSetLength(&newBuf, 13);
+ newName = Tcl_DStringAppend(&newBuf, unsafeInfo[i].cmdName, -1);
+ if (TclRenameCommand(interp, oldName, "___tmp") != TCL_OK
+ || Tcl_HideCommand(interp, "___tmp", newName) != TCL_OK) {
+ Tcl_Panic("problem making 'encoding %s' safe: %s",
+ unsafeInfo[i].cmdName,
+ Tcl_GetString(Tcl_GetObjResult(interp)));
+ }
+ Tcl_CreateObjCommand(interp, oldName, BadEncodingSubcommand,
+ (ClientData) unsafeInfo[i].cmdName, NULL);
}
+ }
+ Tcl_DStringFree(&oldBuf);
+ Tcl_DStringFree(&newBuf);
- Tcl_FreeEncoding(encoding);
- break;
+ /*
+ * Ugh. The [encoding] command is now actually safe, but it is assumed by
+ * scripts that it is not, which messes up security policies.
+ */
+
+ if (Tcl_HideCommand(interp, "encoding", "encoding") != TCL_OK) {
+ Tcl_Panic("problem making 'encoding' safe: %s",
+ Tcl_GetString(Tcl_GetObjResult(interp)));
}
- case ENC_DIRS:
- return EncodingDirsObjCmd(dummy, interp, objc, objv);
- case ENC_NAMES:
- if (objc > 2) {
- Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BadEncodingSubcommand --
+ *
+ * Command used to act as a backstop implementation when subcommands of
+ * "encoding" are unsafe (the real implementations of the subcommands are
+ * hidden). The clientData is always the full official subcommand name.
+ *
+ * Results:
+ * A standard Tcl result (always a TCL_ERROR).
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+BadEncodingSubcommand(
+ ClientData clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *const objv[])
+{
+ const char *subcommandName = (const char *) clientData;
+
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "not allowed to invoke subcommand %s of encoding", subcommandName));
+ Tcl_SetErrorCode(interp, "TCL", "SAFE", "SUBCOMMAND", NULL);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EncodingConvertfromObjCmd --
+ *
+ * This command converts a byte array in an external encoding into a
+ * Tcl string
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+EncodingConvertfromObjCmd(
+ ClientData dummy, /* Not used. */
+ Tcl_Interp *interp, /* Current interpreter. */
+ int objc, /* Number of arguments. */
+ Tcl_Obj *const objv[]) /* Argument objects. */
+{
+ Tcl_Obj *data; /* Byte array to convert */
+ Tcl_DString ds; /* Buffer to hold the string */
+ Tcl_Encoding encoding; /* Encoding to use */
+ int length; /* Length of the byte array being converted */
+ const char *bytesPtr; /* Pointer to the first byte of the array */
+
+ if (objc == 2) {
+ encoding = Tcl_GetEncoding(interp, NULL);
+ data = objv[1];
+ } else if (objc == 3) {
+ if (Tcl_GetEncodingFromObj(interp, objv[1], &encoding) != TCL_OK) {
return TCL_ERROR;
}
- Tcl_GetEncodingNames(interp);
- break;
- case ENC_SYSTEM:
- if (objc > 3) {
- Tcl_WrongNumArgs(interp, 2, objv, "?encoding?");
+ data = objv[2];
+ } else {
+ Tcl_WrongNumArgs(interp, 1, objv, "?encoding? data");
+ return TCL_ERROR;
+ }
+
+ /*
+ * Convert the string into a byte array in 'ds'
+ */
+ bytesPtr = (char *) Tcl_GetByteArrayFromObj(data, &length);
+ Tcl_ExternalToUtfDString(encoding, bytesPtr, length, &ds);
+
+ /*
+ * Note that we cannot use Tcl_DStringResult here because it will
+ * truncate the string at the first null byte.
+ */
+
+ Tcl_SetObjResult(interp, TclDStringToObj(&ds));
+
+ /*
+ * We're done with the encoding
+ */
+
+ Tcl_FreeEncoding(encoding);
+ return TCL_OK;
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EncodingConverttoObjCmd --
+ *
+ * This command converts a Tcl string into a byte array that
+ * encodes the string according to some encoding.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+EncodingConverttoObjCmd(
+ ClientData dummy, /* Not used. */
+ Tcl_Interp *interp, /* Current interpreter. */
+ int objc, /* Number of arguments. */
+ Tcl_Obj *const objv[]) /* Argument objects. */
+{
+ Tcl_Obj *data; /* String to convert */
+ Tcl_DString ds; /* Buffer to hold the byte array */
+ Tcl_Encoding encoding; /* Encoding to use */
+ int length; /* Length of the string being converted */
+ const char *stringPtr; /* Pointer to the first byte of the string */
+
+ /* TODO - ADJUST OBJ INDICES WHEN ENSEMBLIFYING THIS */
+
+ if (objc == 2) {
+ encoding = Tcl_GetEncoding(interp, NULL);
+ data = objv[1];
+ } else if (objc == 3) {
+ if (Tcl_GetEncodingFromObj(interp, objv[1], &encoding) != TCL_OK) {
return TCL_ERROR;
}
- if (objc == 2) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(
- Tcl_GetEncodingName(NULL), -1));
- } else {
- return Tcl_SetSystemEncoding(interp, TclGetString(objv[2]));
- }
- break;
+ data = objv[2];
+ } else {
+ Tcl_WrongNumArgs(interp, 1, objv, "?encoding? data");
+ return TCL_ERROR;
}
+
+ /*
+ * Convert the string to a byte array in 'ds'
+ */
+
+ stringPtr = TclGetStringFromObj(data, &length);
+ Tcl_UtfToExternalDString(encoding, stringPtr, length, &ds);
+ Tcl_SetObjResult(interp,
+ Tcl_NewByteArrayObj((unsigned char*) Tcl_DStringValue(&ds),
+ Tcl_DStringLength(&ds)));
+ Tcl_DStringFree(&ds);
+
+ /*
+ * We're done with the encoding
+ */
+
+ Tcl_FreeEncoding(encoding);
return TCL_OK;
+
}
/*
@@ -641,16 +857,16 @@ EncodingDirsObjCmd(
{
Tcl_Obj *dirListObj;
- if (objc > 3) {
- Tcl_WrongNumArgs(interp, 2, objv, "?dirList?");
+ if (objc > 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "?dirList?");
return TCL_ERROR;
}
- if (objc == 2) {
+ if (objc == 1) {
Tcl_SetObjResult(interp, Tcl_GetEncodingSearchPath());
return TCL_OK;
}
- dirListObj = objv[2];
+ dirListObj = objv[1];
if (Tcl_SetEncodingSearchPath(dirListObj) == TCL_ERROR) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"expected directory list but got \"%s\"",
@@ -664,6 +880,68 @@ EncodingDirsObjCmd(
}
/*
+ *-----------------------------------------------------------------------------
+ *
+ * EncodingNamesObjCmd --
+ *
+ * This command returns a list of the available encoding names
+ *
+ * Results:
+ * Returns a standard Tcl result
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+EncodingNamesObjCmd(ClientData dummy, /* Unused */
+ Tcl_Interp* interp, /* Tcl interpreter */
+ int objc, /* Number of command line args */
+ Tcl_Obj* const objv[]) /* Vector of command line args */
+{
+ if (objc > 1) {
+ Tcl_WrongNumArgs(interp, 1, objv, NULL);
+ return TCL_ERROR;
+ }
+ Tcl_GetEncodingNames(interp);
+ return TCL_OK;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * EncodingSystemObjCmd --
+ *
+ * This command retrieves or changes the system encoding
+ *
+ * Results:
+ * Returns a standard Tcl result
+ *
+ * Side effects:
+ * May change the system encoding.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+EncodingSystemObjCmd(ClientData dummy, /* Unused */
+ Tcl_Interp* interp, /* Tcl interpreter */
+ int objc, /* Number of command line args */
+ Tcl_Obj* const objv[]) /* Vector of command line args */
+{
+ if (objc > 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "?encoding?");
+ return TCL_ERROR;
+ }
+ if (objc == 1) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj(Tcl_GetEncodingName(NULL), -1));
+ } else {
+ return Tcl_SetSystemEncoding(interp, TclGetString(objv[1]));
+ }
+ return TCL_OK;
+}
+
+/*
*----------------------------------------------------------------------
*
* Tcl_ErrorObjCmd --
diff --git a/generic/tclInt.h b/generic/tclInt.h
index 4d3c0b1..3749735 100644
--- a/generic/tclInt.h
+++ b/generic/tclInt.h
@@ -3239,10 +3239,8 @@ MODULE_SCOPE int Tcl_AssembleObjCmd(ClientData clientData,
MODULE_SCOPE int TclNRAssembleObjCmd(ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[]);
-
-MODULE_SCOPE int Tcl_EncodingObjCmd(ClientData clientData,
- Tcl_Interp *interp, int objc,
- Tcl_Obj *const objv[]);
+MODULE_SCOPE Tcl_Command TclInitEncodingCmd(Tcl_Interp *interp);
+MODULE_SCOPE int TclMakeEncodingCommandSafe(Tcl_Interp *interp);
MODULE_SCOPE int Tcl_EofObjCmd(ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[]);
diff --git a/library/init.tcl b/library/init.tcl
index 9ca4514..a202054 100644
--- a/library/init.tcl
+++ b/library/init.tcl
@@ -169,13 +169,7 @@ if {[interp issafe]} {
namespace eval ::tcl::clock [list variable TclLibDir $::tcl_library]
- proc clock args {
- namespace eval ::tcl::clock [list namespace ensemble create -command \
- [uplevel 1 [list namespace origin [lindex [info level 0] 0]]] \
- -subcommands {
- add clicks format microseconds milliseconds scan seconds
- }]
-
+ proc ::tcl::initClock {} {
# Auto-loading stubs for 'clock.tcl'
foreach cmd {add format scan} {
@@ -186,8 +180,9 @@ if {[interp issafe]} {
}
}
- return [uplevel 1 [info level 0]]
+ rename ::tcl::initClock {}
}
+ ::tcl::initClock
}
# Conditionalize for presence of exec.
diff --git a/tests/cmdAH.test b/tests/cmdAH.test
index b4ef605..3c58c1b 100644
--- a/tests/cmdAH.test
+++ b/tests/cmdAH.test
@@ -167,10 +167,10 @@ test cmdAH-3.2 {Tcl_ContinueObjCmd, success} {
test cmdAH-4.1 {Tcl_EncodingObjCmd} -returnCodes error -body {
encoding
-} -result {wrong # args: should be "encoding option ?arg ...?"}
+} -result {wrong # args: should be "encoding subcommand ?arg ...?"}
test cmdAH-4.2 {Tcl_EncodingObjCmd} -returnCodes error -body {
encoding foo
-} -result {bad option "foo": must be convertfrom, convertto, dirs, names, or system}
+} -result {unknown or ambiguous subcommand "foo": must be convertfrom, convertto, dirs, names, or system}
test cmdAH-4.3 {Tcl_EncodingObjCmd} -returnCodes error -body {
encoding convertto
} -result {wrong # args: should be "encoding convertto ?encoding? data"}
diff --git a/tests/interp.test b/tests/interp.test
index 6000ffd..4d61e35 100644
--- a/tests/interp.test
+++ b/tests/interp.test
@@ -20,7 +20,7 @@ catch [list package require -exact Tcltest [info patchlevel]]
testConstraint testinterpdelete [llength [info commands testinterpdelete]]
-set hidden_cmds {cd encoding exec exit fconfigure file glob load open pwd socket source tcl:file:atime tcl:file:attributes tcl:file:copy tcl:file:delete tcl:file:dirname tcl:file:executable tcl:file:exists tcl:file:extension tcl:file:isdirectory tcl:file:isfile tcl:file:link tcl:file:lstat tcl:file:mkdir tcl:file:mtime tcl:file:nativename tcl:file:normalize tcl:file:owned tcl:file:readable tcl:file:readlink tcl:file:rename tcl:file:rootname tcl:file:size tcl:file:stat tcl:file:tail tcl:file:tempfile tcl:file:type tcl:file:volumes tcl:file:writable unload}
+set hidden_cmds {cd encoding exec exit fconfigure file glob load open pwd socket source tcl:encoding:dirs tcl:file:atime tcl:file:attributes tcl:file:copy tcl:file:delete tcl:file:dirname tcl:file:executable tcl:file:exists tcl:file:extension tcl:file:isdirectory tcl:file:isfile tcl:file:link tcl:file:lstat tcl:file:mkdir tcl:file:mtime tcl:file:nativename tcl:file:normalize tcl:file:owned tcl:file:readable tcl:file:readlink tcl:file:rename tcl:file:rootname tcl:file:size tcl:file:stat tcl:file:tail tcl:file:tempfile tcl:file:type tcl:file:volumes tcl:file:writable unload}
foreach i [interp slaves] {
interp delete $i