summaryrefslogtreecommitdiffstats
path: root/library/safe.tcl
diff options
context:
space:
mode:
Diffstat (limited to 'library/safe.tcl')
-rw-r--r--library/safe.tcl969
1 files changed, 317 insertions, 652 deletions
diff --git a/library/safe.tcl b/library/safe.tcl
index 7b85371..2dd4aed 100644
--- a/library/safe.tcl
+++ b/library/safe.tcl
@@ -2,12 +2,12 @@
#
# This file provide a safe loading/sourcing mechanism for safe interpreters.
# It implements a virtual path mecanism to hide the real pathnames from the
-# child. It runs in a parent interpreter and sets up data structure and
-# aliases that will be invoked when used from a child interpreter.
+# slave. It runs in a master interpreter and sets up data structure and
+# aliases that will be invoked when used from a slave interpreter.
#
# See the safe.n man page for details.
#
-# Copyright © 1996-1997 Sun Microsystems, Inc.
+# Copyright (c) 1996-1997 Sun Microsystems, Inc.
#
# See the file "license.terms" for information on usage and redistribution of
# this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -20,7 +20,7 @@
#
# Needed utilities package
-package require opt 0.4.9
+package require opt 0.4.1
# Create the safe namespace
namespace eval ::safe {
@@ -78,40 +78,26 @@ proc ::safe::InterpNested {} {
# Interface/entry point function and front end for "Create"
proc ::safe::interpCreate {args} {
- variable AutoPathSync
- if {$AutoPathSync} {
- set autoPath {}
- }
set Args [::tcl::OptKeyParse ::safe::interpCreate $args]
- RejectExcessColons $child
-
- set withAutoPath [::tcl::OptProcArgGiven -autoPath]
- InterpCreate $child $accessPath \
- [InterpStatics] [InterpNested] $deleteHook $autoPath $withAutoPath
+ InterpCreate $slave $accessPath \
+ [InterpStatics] [InterpNested] $deleteHook
}
proc ::safe::interpInit {args} {
- variable AutoPathSync
- if {$AutoPathSync} {
- set autoPath {}
- }
set Args [::tcl::OptKeyParse ::safe::interpIC $args]
- if {![::interp exists $child]} {
- return -code error "\"$child\" is not an interpreter"
+ if {![::interp exists $slave]} {
+ return -code error "\"$slave\" is not an interpreter"
}
- RejectExcessColons $child
-
- set withAutoPath [::tcl::OptProcArgGiven -autoPath]
- InterpInit $child $accessPath \
- [InterpStatics] [InterpNested] $deleteHook $autoPath $withAutoPath
+ InterpInit $slave $accessPath \
+ [InterpStatics] [InterpNested] $deleteHook
}
-# Check that the given child is "one of us"
-proc ::safe::CheckInterp {child} {
- namespace upvar ::safe [VarName $child] state
- if {![info exists state] || ![::interp exists $child]} {
+# Check that the given slave is "one of us"
+proc ::safe::CheckInterp {slave} {
+ namespace upvar ::safe S$slave state
+ if {![info exists state] || ![::interp exists $slave]} {
return -code error \
- "\"$child\" is not an interpreter managed by ::safe::"
+ "\"$slave\" is not an interpreter managed by ::safe::"
}
}
@@ -129,32 +115,26 @@ proc ::safe::CheckInterp {child} {
# So this will be hopefully written and some integrated with opt1.0
# (hopefully for tcl8.1 ?)
proc ::safe::interpConfigure {args} {
- variable AutoPathSync
switch [llength $args] {
1 {
# If we have exactly 1 argument the semantic is to return all
# the current configuration. We still call OptKeyParse though
- # we know that "child" is our given argument because it also
+ # we know that "slave" is our given argument because it also
# checks for the "-help" option.
set Args [::tcl::OptKeyParse ::safe::interpIC $args]
- CheckInterp $child
- namespace upvar ::safe [VarName $child] state
+ CheckInterp $slave
+ namespace upvar ::safe S$slave state
- set TMP [list \
+ return [join [list \
[list -accessPath $state(access_path)] \
[list -statics $state(staticsok)] \
[list -nested $state(nestedok)] \
- [list -deleteHook $state(cleanupHook)] \
- ]
- if {!$AutoPathSync} {
- lappend TMP [list -autoPath $state(auto_path)]
- }
- return [join $TMP]
+ [list -deleteHook $state(cleanupHook)]]]
}
2 {
# If we have exactly 2 arguments the semantic is a "configure
# get"
- lassign $args child arg
+ lassign $args slave arg
# get the flag sub program (we 'know' about Opt's internal
# representation of data)
@@ -165,36 +145,21 @@ proc ::safe::interpConfigure {args} {
} elseif {$hits == 0} {
return -code error [::tcl::OptFlagUsage $desc $arg]
}
- CheckInterp $child
- namespace upvar ::safe [VarName $child] state
+ CheckInterp $slave
+ namespace upvar ::safe S$slave state
set item [::tcl::OptCurDesc $desc]
set name [::tcl::OptName $item]
switch -exact -- $name {
- -accessPath {
- return [list -accessPath $state(access_path)]
- }
- -autoPath {
- if {$AutoPathSync} {
- return -code error "unknown flag $name (bug)"
- } else {
- return [list -autoPath $state(auto_path)]
- }
- }
- -statics {
- return [list -statics $state(staticsok)]
- }
- -nested {
- return [list -nested $state(nestedok)]
- }
- -deleteHook {
- return [list -deleteHook $state(cleanupHook)]
- }
+ -accessPath {return [list -accessPath $state(access_path)]}
+ -statics {return [list -statics $state(staticsok)]}
+ -nested {return [list -nested $state(nestedok)]}
+ -deleteHook {return [list -deleteHook $state(cleanupHook)]}
-noStatics {
# it is most probably a set in fact but we would need
# then to jump to the set part and it is not *sure*
# that it is a set action that the user want, so force
- # it to use the unambiguous -statics ?value? instead:
+ # it to use the unambigous -statics ?value? instead:
return -code error\
"ambigous query (get or set -noStatics ?)\
use -statics instead"
@@ -213,27 +178,21 @@ proc ::safe::interpConfigure {args} {
# Otherwise we want to parse the arguments like init and
# create did
set Args [::tcl::OptKeyParse ::safe::interpIC $args]
- CheckInterp $child
- namespace upvar ::safe [VarName $child] state
+ CheckInterp $slave
+ namespace upvar ::safe S$slave state
# Get the current (and not the default) values of whatever has
# not been given:
if {![::tcl::OptProcArgGiven -accessPath]} {
- set doreset 0
- set accessPath $state(access_path)
- } else {
set doreset 1
- }
- if {(!$AutoPathSync) && (![::tcl::OptProcArgGiven -autoPath])} {
- set autoPath $state(auto_path)
- } elseif {$AutoPathSync} {
- set autoPath {}
+ set accessPath $state(access_path)
} else {
+ set doreset 0
}
if {
![::tcl::OptProcArgGiven -statics]
&& ![::tcl::OptProcArgGiven -noStatics]
- } then {
+ } {
set statics $state(staticsok)
} else {
set statics [InterpStatics]
@@ -241,7 +200,7 @@ proc ::safe::interpConfigure {args} {
if {
[::tcl::OptProcArgGiven -nested] ||
[::tcl::OptProcArgGiven -nestedLoadOk]
- } then {
+ } {
set nested [InterpNested]
} else {
set nested $state(nestedok)
@@ -249,37 +208,16 @@ proc ::safe::interpConfigure {args} {
if {![::tcl::OptProcArgGiven -deleteHook]} {
set deleteHook $state(cleanupHook)
}
- # Now reconfigure
- set withAutoPath [::tcl::OptProcArgGiven -autoPath]
- InterpSetConfig $child $accessPath $statics $nested $deleteHook $autoPath $withAutoPath
-
- # auto_reset the child (to completely sync the new access_path) tests safe-9.8 safe-9.9
+ # we can now reconfigure :
+ InterpSetConfig $slave $accessPath $statics $nested $deleteHook
+ # auto_reset the slave (to completly synch the new access_path)
if {$doreset} {
- if {[catch {::interp eval $child {auto_reset}} msg]} {
- Log $child "auto_reset failed: $msg"
+ if {[catch {::interp eval $slave {auto_reset}} msg]} {
+ Log $slave "auto_reset failed: $msg"
} else {
- Log $child "successful auto_reset" NOTICE
- }
-
- # Sync the paths used to search for Tcl modules.
- ::interp eval $child {tcl::tm::path remove {*}[tcl::tm::list]}
- if {[llength $state(tm_path_child)] > 0} {
- ::interp eval $child [list \
- ::tcl::tm::add {*}[lreverse $state(tm_path_child)]]
- }
-
- # Remove stale "package ifneeded" data for non-loaded packages.
- # - Not for loaded packages, because "package forget" erases
- # data from "package provide" as well as "package ifneeded".
- # - This is OK because the script cannot reload any version of
- # the package unless it first does "package forget".
- foreach pkg [::interp eval $child {package names}] {
- if {[::interp eval $child [list package provide $pkg]] eq ""} {
- ::interp eval $child [list package forget $pkg]
- }
+ Log $slave "successful auto_reset" NOTICE
}
}
- return
}
}
}
@@ -293,64 +231,54 @@ proc ::safe::interpConfigure {args} {
#
# safe::InterpCreate : doing the real job
#
-# This procedure creates a safe interpreter and initializes it with the safe
+# This procedure creates a safe slave and initializes it with the safe
# base aliases.
-# NB: child name must be simple alphanumeric string, no spaces, no (), no
+# NB: slave name must be simple alphanumeric string, no spaces, no (), no
# {},... {because the state array is stored as part of the name}
#
-# Returns the child name.
+# Returns the slave name.
#
# Optional Arguments :
-# + child name : if empty, generated name will be used
+# + slave name : if empty, generated name will be used
# + access_path: path list controlling where load/source can occur,
-# if empty: the parent auto_path and its subdirectories will be
-# used.
+# if empty: the master auto_path will be used.
# + staticsok : flag, if 0 :no static package can be loaded (load {} Xxx)
# if 1 :static packages are ok.
-# + nestedok : flag, if 0 :no loading to sub-sub interps (load xx xx sub)
+# + nestedok: flag, if 0 :no loading to sub-sub interps (load xx xx sub)
# if 1 : multiple levels are ok.
# use the full name and no indent so auto_mkIndex can find us
proc ::safe::InterpCreate {
- child
+ slave
access_path
staticsok
nestedok
deletehook
- autoPath
- withAutoPath
} {
- # Create the child.
- # If evaluated in ::safe, the interpreter command for foo is ::foo;
- # but for foo::bar is safe::foo::bar. So evaluate in :: instead.
- if {$child ne ""} {
- namespace eval :: [list ::interp create -safe $child]
+ # Create the slave.
+ if {$slave ne ""} {
+ ::interp create -safe $slave
} else {
- # empty argument: generate child name
- set child [::interp create -safe]
+ # empty argument: generate slave name
+ set slave [::interp create -safe]
}
- Log $child "Created" NOTICE
+ Log $slave "Created" NOTICE
- # Initialize it. (returns child name)
- InterpInit $child $access_path $staticsok $nestedok $deletehook $autoPath $withAutoPath
+ # Initialize it. (returns slave name)
+ InterpInit $slave $access_path $staticsok $nestedok $deletehook
}
#
# InterpSetConfig (was setAccessPath) :
-# Sets up child virtual access path and corresponding structure within
-# the parent. Also sets the tcl_library in the child to be the first
+# Sets up slave virtual auto_path and corresponding structure within
+# the master. Also sets the tcl_library in the slave to be the first
# directory in the path.
-# NB: If you change the path after the child has been initialized you
-# probably need to call "auto_reset" in the child in order that it gets
+# NB: If you change the path after the slave has been initialized you
+# probably need to call "auto_reset" in the slave in order that it gets
# the right auto_index() array values.
-#
-# It is the caller's responsibility, if it supplies a non-empty value for
-# access_path, to make the first directory in the path suitable for use as
-# tcl_library, and (if ![setSyncMode]), to set the child's ::auto_path.
-proc ::safe::InterpSetConfig {child access_path staticsok nestedok deletehook autoPath withAutoPath} {
+proc ::safe::InterpSetConfig {slave access_path staticsok nestedok deletehook} {
global auto_path
- variable AutoPathSync
# determine and store the access path if empty
if {$access_path eq ""} {
@@ -359,80 +287,55 @@ proc ::safe::InterpSetConfig {child access_path staticsok nestedok deletehook au
# Make sure that tcl_library is in auto_path and at the first
# position (needed by setAccessPath)
set where [lsearch -exact $access_path [info library]]
- if {$where < 0} {
+ if {$where == -1} {
# not found, add it.
set access_path [linsert $access_path 0 [info library]]
- Log $child "tcl_library was not in auto_path,\
- added it to child's access_path" NOTICE
+ Log $slave "tcl_library was not in auto_path,\
+ added it to slave's access_path" NOTICE
} elseif {$where != 0} {
# not first, move it first
set access_path [linsert \
[lreplace $access_path $where $where] \
0 [info library]]
- Log $child "tcl_libray was not in first in auto_path,\
- moved it to front of child's access_path" NOTICE
+ Log $slave "tcl_libray was not in first in auto_path,\
+ moved it to front of slave's access_path" NOTICE
}
- set raw_auto_path $access_path
-
- # Add 1st level subdirs (will searched by auto loading from tcl
- # code in the child using glob and thus fail, so we add them here
+ # Add 1st level sub dirs (will searched by auto loading from tcl
+ # code in the slave using glob and thus fail, so we add them here
# so by default it works the same).
set access_path [AddSubDirs $access_path]
- } else {
- set raw_auto_path $autoPath
- }
-
- if {$withAutoPath} {
- set raw_auto_path $autoPath
}
- Log $child "Setting accessPath=($access_path) staticsok=$staticsok\
+ Log $slave "Setting accessPath=($access_path) staticsok=$staticsok\
nestedok=$nestedok deletehook=($deletehook)" NOTICE
- if {!$AutoPathSync} {
- Log $child "Setting auto_path=($raw_auto_path)" NOTICE
- }
- namespace upvar ::safe [VarName $child] state
+ namespace upvar ::safe S$slave state
# clear old autopath if it existed
# build new one
# Extend the access list with the paths used to look for Tcl Modules.
# We save the virtual form separately as well, as syncing it with the
- # child has to be defered until the necessary commands are present for
+ # slave has to be defered until the necessary commands are present for
# setup.
+
set norm_access_path {}
- set child_access_path {}
+ set slave_access_path {}
set map_access_path {}
set remap_access_path {}
- set child_tm_path {}
+ set slave_tm_path {}
set i 0
foreach dir $access_path {
set token [PathToken $i]
- lappend child_access_path $token
+ lappend slave_access_path $token
lappend map_access_path $token $dir
lappend remap_access_path $dir $token
lappend norm_access_path [file normalize $dir]
incr i
}
- # Set the child auto_path to a tokenized raw_auto_path.
- # Silently ignore any directories that are not in the access path.
- # If [setSyncMode], SyncAccessPath will overwrite this value with the
- # full access path.
- # If ![setSyncMode], Safe Base code will not change this value.
- set tokens_auto_path {}
- foreach dir $raw_auto_path {
- if {[dict exists $remap_access_path $dir]} {
- lappend tokens_auto_path [dict get $remap_access_path $dir]
- }
- }
- ::interp eval $child [list set auto_path $tokens_auto_path]
-
- # Add the tcl::tm directories to the access path.
set morepaths [::tcl::tm::list]
- set firstpass 1
while {[llength $morepaths]} {
set addpaths $morepaths
set morepaths {}
@@ -441,27 +344,16 @@ proc ::safe::InterpSetConfig {child access_path staticsok nestedok deletehook au
# Prevent the addition of dirs on the tm list to the
# result if they are already known.
if {[dict exists $remap_access_path $dir]} {
- if {$firstpass} {
- # $dir is in [::tcl::tm::list] and belongs in the child_tm_path.
- # Later passes handle subdirectories, which belong in the
- # access path but not in the module path.
- lappend child_tm_path [dict get $remap_access_path $dir]
- }
continue
}
set token [PathToken $i]
lappend access_path $dir
- lappend child_access_path $token
+ lappend slave_access_path $token
lappend map_access_path $token $dir
lappend remap_access_path $dir $token
lappend norm_access_path [file normalize $dir]
- if {$firstpass} {
- # $dir is in [::tcl::tm::list] and belongs in the child_tm_path.
- # Later passes handle subdirectories, which belong in the
- # access path but not in the module path.
- lappend child_tm_path $token
- }
+ lappend slave_tm_path $token
incr i
# [Bug 2854929]
@@ -472,76 +364,44 @@ proc ::safe::InterpSetConfig {child access_path staticsok nestedok deletehook au
# subdirectories.
lappend morepaths {*}[glob -nocomplain -directory $dir -type d *]
}
- set firstpass 0
}
set state(access_path) $access_path
set state(access_path,map) $map_access_path
set state(access_path,remap) $remap_access_path
set state(access_path,norm) $norm_access_path
- set state(access_path,child) $child_access_path
- set state(tm_path_child) $child_tm_path
+ set state(access_path,slave) $slave_access_path
+ set state(tm_path_slave) $slave_tm_path
set state(staticsok) $staticsok
set state(nestedok) $nestedok
set state(cleanupHook) $deletehook
- if {!$AutoPathSync} {
- set state(auto_path) $raw_auto_path
- }
-
- SyncAccessPath $child
- return
+ SyncAccessPath $slave
}
-
#
-# DetokPath:
-# Convert tokens to directories where possible.
-# Leave undefined tokens unconverted. They are
-# nonsense in both the child and the parent.
#
-proc ::safe::DetokPath {child tokenPath} {
- namespace upvar ::safe [VarName $child] state
-
- set childPath {}
- foreach token $tokenPath {
- if {[dict exists $state(access_path,map) $token]} {
- lappend childPath [dict get $state(access_path,map) $token]
- } else {
- lappend childPath $token
- }
- }
- return $childPath
-}
-
-#
-#
-# interpFindInAccessPath:
+# FindInAccessPath:
# Search for a real directory and returns its virtual Id (including the
# "$")
-#
-# When debugging, use TranslatePath for the inverse operation.
-proc ::safe::interpFindInAccessPath {child path} {
- CheckInterp $child
- namespace upvar ::safe [VarName $child] state
+proc ::safe::interpFindInAccessPath {slave path} {
+ namespace upvar ::safe S$slave state
if {![dict exists $state(access_path,remap) $path]} {
- return -code error "$path not found in access path"
+ return -code error "$path not found in access path $access_path"
}
return [dict get $state(access_path,remap) $path]
}
-
#
# addToAccessPath:
# add (if needed) a real directory to access path and return its
# virtual token (including the "$").
-proc ::safe::interpAddToAccessPath {child path} {
+proc ::safe::interpAddToAccessPath {slave path} {
# first check if the directory is already in there
# (inlined interpFindInAccessPath).
- CheckInterp $child
- namespace upvar ::safe [VarName $child] state
+ namespace upvar ::safe S$slave state
if {[dict exists $state(access_path,remap) $path]} {
return [dict get $state(access_path,remap) $path]
@@ -551,12 +411,12 @@ proc ::safe::interpAddToAccessPath {child path} {
set token [PathToken [llength $state(access_path)]]
lappend state(access_path) $path
- lappend state(access_path,child) $token
+ lappend state(access_path,slave) $token
lappend state(access_path,map) $token $path
lappend state(access_path,remap) $path $token
lappend state(access_path,norm) [file normalize $path]
- SyncAccessPath $child
+ SyncAccessPath $slave
return $token
}
@@ -564,88 +424,77 @@ proc ::safe::interpAddToAccessPath {child path} {
# interpreter. It is useful when you want to install the safe base aliases
# into a preexisting safe interpreter.
proc ::safe::InterpInit {
- child
+ slave
access_path
staticsok
nestedok
deletehook
- autoPath
- withAutoPath
} {
# Configure will generate an access_path when access_path is empty.
- InterpSetConfig $child $access_path $staticsok $nestedok $deletehook $autoPath $withAutoPath
+ InterpSetConfig $slave $access_path $staticsok $nestedok $deletehook
# NB we need to add [namespace current], aliases are always absolute
# paths.
- # These aliases let the child load files to define new commands
- # This alias lets the child use the encoding names, convertfrom,
+ # These aliases let the slave load files to define new commands
+ # This alias lets the slave use the encoding names, convertfrom,
# convertto, and system, but not "encoding system <name>" to set the
# system encoding.
# Handling Tcl Modules, we need a restricted form of Glob.
# This alias interposes on the 'exit' command and cleanly terminates
- # the child.
+ # the slave.
foreach {command alias} {
source AliasSource
load AliasLoad
+ encoding AliasEncoding
exit interpDelete
glob AliasGlob
} {
- ::interp alias $child $command {} [namespace current]::$alias $child
+ ::interp alias $slave $command {} [namespace current]::$alias $slave
}
- # UGLY POINT! These commands are safe (they're ensembles with unsafe
- # subcommands), but is assumed to not be by existing policies so it is
- # hidden by default. Hack it...
- foreach command {encoding file} {
- ::interp alias $child $command {} interp invokehidden $child $command
- }
-
- # This alias lets the child have access to a subset of the 'file'
+ # This alias lets the slave have access to a subset of the 'file'
# command functionality.
- foreach subcommand {dirname extension rootname tail} {
- ::interp alias $child ::tcl::file::$subcommand {} \
- ::safe::AliasFileSubcommand $child $subcommand
- }
-
- # Subcommand of 'encoding' that has special handling; [encoding system] is
- # OK provided it has no other arguments passed to it.
- ::interp alias $child ::tcl::encoding::system {} \
- ::safe::AliasEncodingSystem $child
+ AliasSubset $slave file \
+ file dir.* join root.* ext.* tail path.* split
# Subcommands of info
- ::interp alias $child ::tcl::info::nameofexecutable {} \
- ::safe::AliasExeName $child
+ foreach {subcommand alias} {
+ nameofexecutable AliasExeName
+ } {
+ ::interp alias $slave ::tcl::info::$subcommand \
+ {} [namespace current]::$alias $slave
+ }
- # The allowed child variables already have been set by Tcl_MakeSafe(3)
+ # The allowed slave variables already have been set by Tcl_MakeSafe(3)
- # Source init.tcl and tm.tcl into the child, to get auto_load and
+ # Source init.tcl and tm.tcl into the slave, to get auto_load and
# other procedures defined:
- if {[catch {::interp eval $child {
+ if {[catch {::interp eval $slave {
source [file join $tcl_library init.tcl]
- }} msg opt]} {
- Log $child "can't source init.tcl ($msg)"
- return -options $opt "can't source init.tcl into child $child ($msg)"
+ }} msg]} {
+ Log $slave "can't source init.tcl ($msg)"
+ return -code error "can't source init.tcl into slave $slave ($msg)"
}
- if {[catch {::interp eval $child {
+ if {[catch {::interp eval $slave {
source [file join $tcl_library tm.tcl]
- }} msg opt]} {
- Log $child "can't source tm.tcl ($msg)"
- return -options $opt "can't source tm.tcl into child $child ($msg)"
+ }} msg]} {
+ Log $slave "can't source tm.tcl ($msg)"
+ return -code error "can't source tm.tcl into slave $slave ($msg)"
}
# Sync the paths used to search for Tcl modules. This can be done only
# now, after tm.tcl was loaded.
- namespace upvar ::safe [VarName $child] state
- if {[llength $state(tm_path_child)] > 0} {
- ::interp eval $child [list \
- ::tcl::tm::add {*}[lreverse $state(tm_path_child)]]
+ namespace upvar ::safe S$slave state
+ if {[llength $state(tm_path_slave)] > 0} {
+ ::interp eval $slave [list \
+ ::tcl::tm::add {*}[lreverse $state(tm_path_slave)]]
}
- return $child
+ return $slave
}
# Add (only if needed, avoid duplicates) 1 level of sub directories to an
@@ -671,31 +520,16 @@ proc ::safe::AddSubDirs {pathList} {
return $res
}
-# This procedure deletes a safe interpreter managed by Safe Tcl and cleans up
-# associated state.
-# - The command will also delete non-Safe-Base interpreters.
-# - This is regrettable, but to avoid breaking existing code this should be
-# amended at the next major revision by uncommenting "CheckInterp".
-
-proc ::safe::interpDelete {child} {
- Log $child "About to delete" NOTICE
-
- # CheckInterp $child
- namespace upvar ::safe [VarName $child] state
-
- # When an interpreter is deleted with [interp delete], any sub-interpreters
- # are deleted automatically, but this leaves behind their data in the Safe
- # Base. To clean up properly, we call safe::interpDelete recursively on each
- # Safe Base sub-interpreter, so each one is deleted cleanly and not by
- # the automatic mechanism built into [interp delete].
- foreach sub [interp children $child] {
- if {[info exists ::safe::[VarName [list $child $sub]]]} {
- ::safe::interpDelete [list $child $sub]
- }
- }
+# This procedure deletes a safe slave managed by Safe Tcl and cleans up
+# associated state:
- # If the child has a cleanup hook registered, call it. Check the
- # existence because we might be called to delete an interp which has
+proc ::safe::interpDelete {slave} {
+ Log $slave "About to delete" NOTICE
+
+ namespace upvar ::safe S$slave state
+
+ # If the slave has a cleanup hook registered, call it. Check the
+ # existance because we might be called to delete an interp which has
# not been registered with us at all
if {[info exists state(cleanupHook)]} {
@@ -704,15 +538,15 @@ proc ::safe::interpDelete {child} {
# remove the hook now, otherwise if the hook calls us somehow,
# we'll loop
unset state(cleanupHook)
- try {
- {*}$hook $child
- } on error err {
- Log $child "Delete hook error ($err)"
+ if {[catch {
+ {*}$hook $slave
+ } err]} {
+ Log $slave "Delete hook error ($err)"
}
}
}
- # Discard the global array of state associated with the child, and
+ # Discard the global array of state associated with the slave, and
# delete the interpreter.
if {[info exists state]} {
@@ -721,15 +555,15 @@ proc ::safe::interpDelete {child} {
# if we have been called twice, the interp might have been deleted
# already
- if {[::interp exists $child]} {
- ::interp delete $child
- Log $child "Deleted" NOTICE
+ if {[::interp exists $slave]} {
+ ::interp delete $slave
+ Log $slave "Deleted" NOTICE
}
return
}
-# Set (or get) the logging mechanism
+# Set (or get) the logging mecanism
proc ::safe::setLogCmd {args} {
variable Log
@@ -749,9 +583,9 @@ proc ::safe::setLogCmd {args} {
} else {
# Activate logging, define proper command.
- proc ::safe::Log {child msg {type ERROR}} {
+ proc ::safe::Log {slave msg {type ERROR}} {
variable Log
- {*}$Log "$type for child $child : $msg"
+ {*}$Log "$type for slave $slave : $msg"
return
}
}
@@ -760,28 +594,24 @@ proc ::safe::setLogCmd {args} {
# ------------------- END OF PUBLIC METHODS ------------
#
-# Sets the child auto_path to its recorded access path. Also sets
-# tcl_library to the first token of the access path.
+# Sets the slave auto_path to the master recorded value. Also sets
+# tcl_library to the first token of the virtual path.
#
-proc ::safe::SyncAccessPath {child} {
- variable AutoPathSync
- namespace upvar ::safe [VarName $child] state
+proc ::safe::SyncAccessPath {slave} {
+ namespace upvar ::safe S$slave state
- set child_access_path $state(access_path,child)
- if {$AutoPathSync} {
- ::interp eval $child [list set auto_path $child_access_path]
+ set slave_access_path $state(access_path,slave)
+ ::interp eval $slave [list set auto_path $slave_access_path]
- Log $child "auto_path in $child has been set to $child_access_path"\
- NOTICE
- }
+ Log $slave "auto_path in $slave has been set to $slave_access_path"\
+ NOTICE
# This code assumes that info library is the first element in the
- # list of access path's. See -> InterpSetConfig for the code which
+ # list of auto_path's. See -> InterpSetConfig for the code which
# ensures this condition.
- ::interp eval $child [list \
- set tcl_library [lindex $child_access_path 0]]
- return
+ ::interp eval $slave [list \
+ set tcl_library [lindex $slave_access_path 0]]
}
# Returns the virtual token for directory number N.
@@ -794,8 +624,8 @@ proc ::safe::PathToken {n} {
#
# translate virtual path into real path
#
-proc ::safe::TranslatePath {child path} {
- namespace upvar ::safe [VarName $child] state
+proc ::safe::TranslatePath {slave path} {
+ namespace upvar ::safe S$slave state
# somehow strip the namespaces 'functionality' out (the danger is that
# we would strip valid macintosh "../" queries... :
@@ -810,7 +640,7 @@ proc ::safe::TranslatePath {child path} {
# file name control (limit access to files/resources that should be a
# valid tcl source file)
-proc ::safe::CheckFileName {child file} {
+proc ::safe::CheckFileName {slave file} {
# This used to limit what can be sourced to ".tcl" and forbid files
# with more than 1 dot and longer than 14 chars, but I changed that
# for 8.4 as a safe interp has enough internal protection already to
@@ -827,22 +657,9 @@ proc ::safe::CheckFileName {child file} {
}
}
-# AliasFileSubcommand handles selected subcommands of [file] in safe
-# interpreters that are *almost* safe. In particular, it just acts to
-# prevent discovery of what home directories exist.
-
-proc ::safe::AliasFileSubcommand {child subcommand name} {
- if {[string match ~* $name]} {
- set name ./$name
- }
- tailcall ::interp invokehidden $child tcl:file:$subcommand $name
-}
-
# AliasGlob is the target of the "glob" alias in safe interpreters.
-
-proc ::safe::AliasGlob {child args} {
- variable AutoPathSync
- Log $child "GLOB ! $args" NOTICE
+proc ::safe::AliasGlob {slave args} {
+ Log $slave "GLOB ! $args" NOTICE
set cmd {}
set at 0
array set got {
@@ -864,15 +681,11 @@ proc ::safe::AliasGlob {child args} {
while {$at < [llength $args]} {
switch -glob -- [set opt [lindex $args $at]] {
- -nocomplain - -- - -tails {
+ -nocomplain - -- - -join - -tails {
lappend cmd $opt
set got($opt) 1
incr at
}
- -join {
- set got($opt) 1
- incr at
- }
-types - -type {
lappend cmd -types [lindex $args [incr at]]
incr at
@@ -886,10 +699,16 @@ proc ::safe::AliasGlob {child args} {
set virtualdir [lindex $args [incr at]]
incr at
}
+ pkgIndex.tcl {
+ # Oops, this is globbing a subdirectory in regular package
+ # search. That is not wanted. Abort, handler does catch
+ # already (because glob was not defined before). See
+ # package.tcl, lines 484ff in tclPkgUnknown.
+ return -code error "unknown command glob"
+ }
-* {
- Log $child "Safe base rejecting glob option '$opt'"
+ Log $slave "Safe base rejecting glob option '$opt'"
return -code error "Safe base rejecting glob option '$opt'"
- # unsafe/unnecessary options rejected: -path
}
default {
break
@@ -899,132 +718,90 @@ proc ::safe::AliasGlob {child args} {
}
# Get the real path from the virtual one and check that the path is in the
- # access path of that child. Done after basic argument processing so that
+ # access path of that slave. Done after basic argument processing so that
# we know if -nocomplain is set.
if {$got(-directory)} {
- try {
- set dir [TranslatePath $child $virtualdir]
- DirInAccessPath $child $dir
- } on error msg {
- Log $child $msg
- if {$got(-nocomplain)} return
+ if {[catch {
+ set dir [TranslatePath $slave $virtualdir]
+ DirInAccessPath $slave $dir
+ } msg]} {
+ Log $slave $msg
+ if {$got(-nocomplain)} {
+ return
+ }
return -code error "permission denied"
}
- if {$got(--)} {
- set cmd [linsert $cmd end-1 -directory $dir]
- } else {
- lappend cmd -directory $dir
- }
- } else {
- # The code after this "if ... else" block would conspire to return with
- # no results in this case, if it were allowed to proceed. Instead,
- # return now and reduce the number of cases to be considered later.
- Log $child {option -directory must be supplied}
- if {$got(-nocomplain)} return
- return -code error "permission denied"
+ lappend cmd -directory $dir
}
- # Apply the -join semantics ourselves (hence -join not copied to $cmd)
+ # Apply the -join semantics ourselves
if {$got(-join)} {
set args [lreplace $args $at end [join [lrange $args $at end] "/"]]
}
- # Process the pattern arguments. If we've done a join there is only one
- # pattern argument.
-
+ # Process remaining pattern arguments
set firstPattern [llength $cmd]
foreach opt [lrange $args $at end] {
if {![regexp $dirPartRE $opt -> thedir thefile]} {
set thedir .
- # The *.tm search comes here.
}
- # "Special" treatment for (joined) argument {*/pkgIndex.tcl}.
- # Do the expansion of "*" here, and filter out any directories that are
- # not in the access path. The outcome is to lappend to cmd a path of
- # the form $virtualdir/subdir/pkgIndex.tcl for each subdirectory subdir,
- # after removing any subdir that are not in the access path.
- if {($thedir eq "*") && ($thefile eq "pkgIndex.tcl")} {
+ if {$thedir eq "*"} {
set mapped 0
- foreach d [glob -directory [TranslatePath $child $virtualdir] \
+ foreach d [glob -directory [TranslatePath $slave $virtualdir] \
-types d -tails *] {
catch {
- DirInAccessPath $child \
- [TranslatePath $child [file join $virtualdir $d]]
- lappend cmd [file join $d $thefile]
- set mapped 1
+ DirInAccessPath $slave \
+ [TranslatePath $slave [file join $virtualdir $d]]
+ if {$thefile eq "pkgIndex.tcl" || $thefile eq "*.tm"} {
+ lappend cmd [file join $d $thefile]
+ set mapped 1
+ }
}
}
if {$mapped} continue
- # Don't [continue] if */pkgIndex.tcl has no matches in the access
- # path. The pattern will now receive the same treatment as a
- # "non-special" pattern (and will fail because it includes a "*" in
- # the directory name).
}
- # Any directory pattern that is not an exact (i.e. non-glob) match to a
- # directory in the access path will be rejected here.
- # - Rejections include any directory pattern that has glob matching
- # patterns "*", "?", backslashes, braces or square brackets, (UNLESS
- # it corresponds to a genuine directory name AND that directory is in
- # the access path).
- # - The only "special matching characters" that remain in patterns for
- # processing by glob are in the filename tail.
- # - [file join $anything ~${foo}] is ~${foo}, which is not an exact
- # match to any directory in the access path. Hence directory patterns
- # that begin with "~" are rejected here. Tests safe-16.[5-8] check
- # that "file join" remains as required and does not expand ~${foo}.
- # - Bug [3529949] relates to unwanted expansion of ~${foo} and this is
- # how the present code avoids the bug. All tests safe-16.* relate.
- try {
- DirInAccessPath $child [TranslatePath $child \
- [file join $virtualdir $thedir]]
- } on error msg {
- Log $child $msg
+ if {[catch {
+ set thedir [file join $virtualdir $thedir]
+ DirInAccessPath $slave [TranslatePath $slave $thedir]
+ } msg]} {
+ Log $slave $msg
if {$got(-nocomplain)} continue
return -code error "permission denied"
}
lappend cmd $opt
}
- Log $child "GLOB = $cmd" NOTICE
+ Log $slave "GLOB = $cmd" NOTICE
if {$got(-nocomplain) && [llength $cmd] eq $firstPattern} {
return
}
- try {
- # >>>>>>>>>> HERE'S THE CALL TO SAFE INTERP GLOB <<<<<<<<<<
- # - Pattern arguments added to cmd have NOT been translated from tokens.
- # Only the virtualdir is translated (to dir).
- # - In the pkgIndex.tcl case, there is no "*" in the pattern arguments,
- # which are a list of names each with tail pkgIndex.tcl. The purpose
- # of the call to glob is to remove the names for which the file does
- # not exist.
- set entries [::interp invokehidden $child glob {*}$cmd]
- } on error msg {
- # This is the only place that a call with -nocomplain and no invalid
- # "dash-options" can return an error.
- Log $child $msg
+ if {[catch {
+ ::interp invokehidden $slave glob {*}$cmd
+ } msg]} {
+ Log $slave $msg
return -code error "script error"
}
- Log $child "GLOB < $entries" NOTICE
+ Log $slave "GLOB < $msg" NOTICE
- # Translate path back to what the child should see.
+ # Translate path back to what the slave should see.
set res {}
set l [string length $dir]
- foreach p $entries {
+ foreach p $msg {
if {[string equal -length $l $dir $p]} {
set p [string replace $p 0 [expr {$l-1}] $virtualdir]
}
lappend res $p
}
- Log $child "GLOB > $res" NOTICE
+ Log $slave "GLOB > $res" NOTICE
return $res
}
# AliasSource is the target of the "source" alias in safe interpreters.
-proc ::safe::AliasSource {child args} {
+proc ::safe::AliasSource {slave args} {
set argc [llength $args]
# Extended for handling of Tcl Modules to allow not only "source
# filename", but "source -encoding E filename" as well.
@@ -1033,87 +810,83 @@ proc ::safe::AliasSource {child args} {
set encoding [lindex $args 1]
set at 2
if {$encoding eq "identity"} {
- Log $child "attempt to use the identity encoding"
+ Log $slave "attempt to use the identity encoding"
return -code error "permission denied"
}
} else {
set at 0
- set encoding utf-8
+ set encoding {}
}
if {$argc != 1} {
set msg "wrong # args: should be \"source ?-encoding E? fileName\""
- Log $child "$msg ($args)"
+ Log $slave "$msg ($args)"
return -code error $msg
}
set file [lindex $args $at]
# get the real path from the virtual one.
if {[catch {
- set realfile [TranslatePath $child $file]
+ set realfile [TranslatePath $slave $file]
} msg]} {
- Log $child $msg
+ Log $slave $msg
return -code error "permission denied"
}
- # check that the path is in the access path of that child
+ # check that the path is in the access path of that slave
if {[catch {
- FileInAccessPath $child $realfile
+ FileInAccessPath $slave $realfile
} msg]} {
- Log $child $msg
+ Log $slave $msg
return -code error "permission denied"
}
- # Check that the filename exists and is readable. If it is not, deliver
- # this -errorcode so that caller in tclPkgUnknown does not write a message
- # to tclLog. Has no effect on other callers of ::source, which are in
- # "package ifneeded" scripts.
+ # do the checks on the filename :
if {[catch {
- CheckFileName $child $realfile
+ CheckFileName $slave $realfile
} msg]} {
- Log $child "$realfile:$msg"
- return -code error -errorcode {POSIX EACCES} $msg
+ Log $slave "$realfile:$msg"
+ return -code error $msg
}
# Passed all the tests, lets source it. Note that we do this all manually
- # because we want to control [info script] in the child so information
+ # because we want to control [info script] in the slave so information
# doesn't leak so much. [Bug 2913625]
- set old [::interp eval $child {info script}]
- set replacementMsg "script error"
+ set old [::interp eval $slave {info script}]
set code [catch {
set f [open $realfile]
- fconfigure $f -encoding $encoding -eofchar "\x1A {}"
+ fconfigure $f -eofchar \032
+ if {$encoding ne ""} {
+ fconfigure $f -encoding $encoding
+ }
set contents [read $f]
close $f
- ::interp eval $child [list info script $file]
+ ::interp eval $slave [list info script $file]
+ ::interp eval $slave $contents
} msg opt]
- if {$code == 0} {
- set code [catch {::interp eval $child $contents} msg opt]
- set replacementMsg $msg
- }
- catch {interp eval $child [list info script $old]}
+ catch {interp eval $slave [list info script $old]}
# Note that all non-errors are fine result codes from [source], so we must
# take a little care to do it properly. [Bug 2923613]
if {$code == 1} {
- Log $child $msg
- return -code error $replacementMsg
+ Log $slave $msg
+ return -code error "script error"
}
return -code $code -options $opt $msg
}
# AliasLoad is the target of the "load" alias in safe interpreters.
-proc ::safe::AliasLoad {child file args} {
+proc ::safe::AliasLoad {slave file args} {
set argc [llength $args]
if {$argc > 2} {
set msg "load error: too many arguments"
- Log $child "$msg ($argc) {$file $args}"
+ Log $slave "$msg ($argc) {$file $args}"
return -code error $msg
}
- # prefix (can be empty if file is not).
- set prefix [lindex $args 0]
+ # package name (can be empty if file is not).
+ set package [lindex $args 0]
- namespace upvar ::safe [VarName $child] state
+ namespace upvar ::safe S$slave state
# Determine where to load. load use a relative interp path and {}
# means self, so we can directly and safely use passed arg.
@@ -1122,67 +895,62 @@ proc ::safe::AliasLoad {child file args} {
# we will try to load into a sub sub interp; check that we want to
# authorize that.
if {!$state(nestedok)} {
- Log $child "loading to a sub interp (nestedok)\
- disabled (trying to load $prefix to $target)"
+ Log $slave "loading to a sub interp (nestedok)\
+ disabled (trying to load $package to $target)"
return -code error "permission denied (nested load)"
}
}
# Determine what kind of load is requested
if {$file eq ""} {
- # static loading
- if {$prefix eq ""} {
- set msg "load error: empty filename and no prefix"
- Log $child $msg
+ # static package loading
+ if {$package eq ""} {
+ set msg "load error: empty filename and no package name"
+ Log $slave $msg
return -code error $msg
}
if {!$state(staticsok)} {
- Log $child "static loading disabled\
- (trying to load $prefix to $target)"
- return -code error "permission denied (static library)"
+ Log $slave "static packages loading disabled\
+ (trying to load $package to $target)"
+ return -code error "permission denied (static package)"
}
} else {
# file loading
# get the real path from the virtual one.
- try {
- set file [TranslatePath $child $file]
- } on error msg {
- Log $child $msg
+ if {[catch {
+ set file [TranslatePath $slave $file]
+ } msg]} {
+ Log $slave $msg
return -code error "permission denied"
}
# check the translated path
- try {
- FileInAccessPath $child $file
- } on error msg {
- Log $child $msg
+ if {[catch {
+ FileInAccessPath $slave $file
+ } msg]} {
+ Log $slave $msg
return -code error "permission denied (path)"
}
}
- try {
- return [::interp invokehidden $child load $file $prefix $target]
- } on error msg {
- # Some libraries return no error message.
- set msg0 "load of library for prefix $prefix failed"
- if {$msg eq {}} {
- set msg $msg0
- } else {
- set msg "$msg0: $msg"
- }
- Log $child $msg
+ if {[catch {
+ ::interp invokehidden $slave load $file $package $target
+ } msg]} {
+ Log $slave $msg
return -code error $msg
}
+
+ return $msg
}
# FileInAccessPath raises an error if the file is not found in the list of
-# directories contained in the (parent side recorded) child's access path.
+# directories contained in the (master side recorded) slave's access path.
# the security here relies on "file dirname" answering the proper
# result... needs checking ?
-proc ::safe::FileInAccessPath {child file} {
- namespace upvar ::safe [VarName $child] state
+proc ::safe::FileInAccessPath {slave file} {
+ namespace upvar ::safe S$slave state
set access_path $state(access_path)
if {[file isdirectory $file]} {
@@ -1194,14 +962,14 @@ proc ::safe::FileInAccessPath {child file} {
# potential pathname anomalies.
set norm_parent [file normalize $parent]
- namespace upvar ::safe [VarName $child] state
+ namespace upvar ::safe S$slave state
if {$norm_parent ni $state(access_path,norm)} {
return -code error "\"$file\": not in access_path"
}
}
-proc ::safe::DirInAccessPath {child dir} {
- namespace upvar ::safe [VarName $child] state
+proc ::safe::DirInAccessPath {slave dir} {
+ namespace upvar ::safe S$slave state
set access_path $state(access_path)
if {[file isfile $dir]} {
@@ -1212,94 +980,71 @@ proc ::safe::DirInAccessPath {child dir} {
# potential pathname anomalies.
set norm_dir [file normalize $dir]
- namespace upvar ::safe [VarName $child] state
+ namespace upvar ::safe S$slave state
if {$norm_dir ni $state(access_path,norm)} {
return -code error "\"$dir\": not in access_path"
}
}
-# This procedure is used to report an attempt to use an unsafe member of an
-# ensemble command.
-
-proc ::safe::BadSubcommand {child command subcommand args} {
- set msg "not allowed to invoke subcommand $subcommand of $command"
- Log $child $msg
- return -code error -errorcode {TCL SAFE SUBCOMMAND} $msg
-}
+# This procedure enables access from a safe interpreter to only a subset
+# of the subcommands of a command:
-# AliasEncodingSystem is the target of the "encoding system" alias in safe
-# interpreters.
-proc ::safe::AliasEncodingSystem {child args} {
- try {
- # Must not pass extra arguments; safe interpreters may not set the
- # system encoding but they may read it.
- if {[llength $args]} {
- return -code error -errorcode {TCL WRONGARGS} \
- "wrong # args: should be \"encoding system\""
- }
- } on error {msg options} {
- Log $child $msg
- return -options $options $msg
+proc ::safe::Subset {slave command okpat args} {
+ set subcommand [lindex $args 0]
+ if {[regexp $okpat $subcommand]} {
+ return [$command {*}$args]
}
- tailcall ::interp invokehidden $child tcl:encoding:system
+ set msg "not allowed to invoke subcommand $subcommand of $command"
+ Log $slave $msg
+ return -code error $msg
}
-# Various minor hiding of platform features. [Bug 2913625]
+# This procedure installs an alias in a slave that invokes "safesubset" in
+# the master to execute allowed subcommands. It precomputes the pattern of
+# allowed subcommands; you can use wildcards in the pattern if you wish to
+# allow subcommand abbreviation.
+#
+# Syntax is: AliasSubset slave alias target subcommand1 subcommand2...
-proc ::safe::AliasExeName {child} {
- return ""
+proc ::safe::AliasSubset {slave alias target args} {
+ set pat "^([join $args |])\$"
+ ::interp alias $slave $alias {}\
+ [namespace current]::Subset $slave $target $pat
}
-# ------------------------------------------------------------------------------
-# Using Interpreter Names with Namespace Qualifiers
-# ------------------------------------------------------------------------------
-# (1) We wish to preserve compatibility with existing code, in which Safe Base
-# interpreter names have no namespace qualifiers.
-# (2) safe::interpCreate and the rest of the Safe Base previously could not
-# accept namespace qualifiers in an interpreter name.
-# (3) The interp command will accept namespace qualifiers in an interpreter
-# name, but accepts distinct interpreters that will have the same command
-# name (e.g. foo, ::foo, and :::foo) (bug 66c2e8c974).
-# (4) To satisfy these constraints, Safe Base interpreter names will be fully
-# qualified namespace names with no excess colons and with the leading "::"
-# omitted.
-# (5) Trailing "::" implies a namespace tail {}, which interp reads as {{}}.
-# Reject such names.
-# (6) We could:
-# (a) EITHER reject usable but non-compliant names (e.g. excess colons) in
-# interpCreate, interpInit;
-# (b) OR accept such names and then translate to a compliant name in every
-# command.
-# The problem with (b) is that the user will expect to use the name with the
-# interp command and will find that it is not recognised.
-# E.g "interpCreate ::foo" creates interpreter "foo", and the user's name
-# "::foo" works with all the Safe Base commands, but "interp eval ::foo"
-# fails.
-# So we choose (a).
-# (7) The command
-# namespace upvar ::safe S$child state
-# becomes
-# namespace upvar ::safe [VarName $child] state
-# ------------------------------------------------------------------------------
-
-proc ::safe::RejectExcessColons {child} {
- set stripped [regsub -all -- {:::*} $child ::]
- if {[string range $stripped end-1 end] eq {::}} {
- return -code error {interpreter name must not end in "::"}
- }
- if {$stripped ne $child} {
- set msg {interpreter name has excess colons in namespace separators}
- return -code error $msg
+# AliasEncoding is the target of the "encoding" alias in safe interpreters.
+
+proc ::safe::AliasEncoding {slave option args} {
+ # Careful; do not want empty option to get through to the [string equal]
+ if {[regexp {^(name.*|convert.*|)$} $option]} {
+ return [::interp invokehidden $slave encoding $option {*}$args]
}
- if {[string range $stripped 0 1] eq {::}} {
- return -code error {interpreter name must not begin "::"}
+
+ if {[string equal -length [string length $option] $option "system"]} {
+ if {[llength $args] == 0} {
+ # passed all the tests , lets source it:
+ if {[catch {
+ set sysenc [::interp invokehidden $slave encoding system]
+ } msg]} {
+ Log $slave $msg
+ return -code error "script error"
+ }
+ return $sysenc
+ }
+ set msg "wrong # args: should be \"encoding system\""
+ set code {TCL WRONGARGS}
+ } else {
+ set msg "bad option \"$option\": must be convertfrom, convertto, names, or system"
+ set code [list TCL LOOKUP INDEX option $option]
}
- return
+ Log $slave $msg
+ return -code error -errorcode $code $msg
}
-proc ::safe::VarName {child} {
- # return S$child
- return S[string map {:: @N @ @A} $child]
+# Various minor hiding of platform features. [Bug 2913625]
+
+proc ::safe::AliasExeName {slave} {
+ return ""
}
proc ::safe::Setup {} {
@@ -1308,34 +1053,29 @@ proc ::safe::Setup {} {
# Setup the arguments parsing
#
####
- variable AutoPathSync
# Share the descriptions
- set OptList {
- {-accessPath -list {} "access path for the child"}
+ set temp [::tcl::OptKeyRegister {
+ {-accessPath -list {} "access path for the slave"}
{-noStatics "prevent loading of statically linked pkgs"}
{-statics true "loading of statically linked pkgs"}
{-nestedLoadOk "allow nested loading"}
{-nested false "nested loading"}
{-deleteHook -script {} "delete hook"}
- }
- if {!$AutoPathSync} {
- lappend OptList {-autoPath -list {} "::auto_path for the child"}
- }
- set temp [::tcl::OptKeyRegister $OptList]
+ }]
- # create case (child is optional)
+ # create case (slave is optional)
::tcl::OptKeyRegister {
- {?child? -name {} "name of the child (optional)"}
+ {?slave? -name {} "name of the slave (optional)"}
} ::safe::interpCreate
# adding the flags sub programs to the command program (relying on Opt's
# internal implementation details)
lappend ::tcl::OptDesc(::safe::interpCreate) $::tcl::OptDesc($temp)
- # init and configure (child is needed)
+ # init and configure (slave is needed)
::tcl::OptKeyRegister {
- {child -name {} "name of the child"}
+ {slave -name {} "name of the slave"}
} ::safe::interpIC
# adding the flags sub programs to the command program (relying on Opt's
@@ -1359,104 +1099,29 @@ proc ::safe::Setup {} {
return
}
-# Accessor method for ::safe::AutoPathSync
-# Usage: ::safe::setSyncMode ?newValue?
-# Respond to changes by calling Setup again, preserving any
-# caller-defined logging. This allows complete equivalence with
-# prior Safe Base behavior if AutoPathSync is true.
-#
-# >>> WARNING <<<
-#
-# DO NOT CHANGE AutoPathSync EXCEPT BY THIS COMMAND - IT IS VITAL THAT WHENEVER
-# THE VALUE CHANGES, THE EXISTING PARSE TOKENS ARE DELETED AND Setup IS CALLED
-# AGAIN.
-# (The initialization of AutoPathSync at the end of this file is acceptable
-# because Setup has not yet been called.)
-
-proc ::safe::setSyncMode {args} {
- variable AutoPathSync
-
- if {[llength $args] == 0} {
- } elseif {[llength $args] == 1} {
- set newValue [lindex $args 0]
- if {![string is boolean -strict $newValue]} {
- return -code error "new value must be a valid boolean"
- }
- set args [expr {$newValue && $newValue}]
- if {([info vars ::safe::S*] ne {}) && ($args != $AutoPathSync)} {
- return -code error \
- "cannot set new value while Safe Base child interpreters exist"
- }
- if {($args != $AutoPathSync)} {
- set AutoPathSync {*}$args
- ::tcl::OptKeyDelete ::safe::interpCreate
- ::tcl::OptKeyDelete ::safe::interpIC
- set TmpLog [setLogCmd]
- Setup
- setLogCmd $TmpLog
- }
- } else {
- set msg {wrong # args: should be "safe::setSyncMode ?newValue?"}
- return -code error $msg
- }
-
- return $AutoPathSync
-}
-
namespace eval ::safe {
- # internal variables (must not begin with "S")
-
- # AutoPathSync
- #
- # Set AutoPathSync to 0 to give a child's ::auto_path the same meaning as
- # for an unsafe interpreter: the package command will search its directories
- # and first-level subdirectories for pkgIndex.tcl files; the auto-loader
- # will search its directories for tclIndex files. The access path and
- # module path will be maintained as separate values, and ::auto_path will
- # not be updated when the user calls ::safe::interpAddToAccessPath to add to
- # the access path. If the user specifies an access path when calling
- # interpCreate, interpInit or interpConfigure, it is the user's
- # responsibility to define the child's auto_path. If these commands are
- # called with no (or empty) access path, the child's auto_path will be set
- # to a tokenized form of the parent's auto_path, and these directories and
- # their first-level subdirectories will be added to the access path.
- #
- # Set to 1 for "traditional" behavior: a child's entire access path and
- # module path are copied to its ::auto_path, which is updated whenever
- # the user calls ::safe::interpAddToAccessPath to add to the access path.
- variable AutoPathSync 1
+ # internal variables
# Log command, set via 'setLogCmd'. Logging is disabled when empty.
variable Log {}
- # The package maintains a state array per child interp under its
+ # The package maintains a state array per slave interp under its
# control. The name of this array is S<interp-name>. This array is
# brought into scope where needed, using 'namespace upvar'. The S
- # prefix is used to avoid that a child interp called "Log" smashes
+ # prefix is used to avoid that a slave interp called "Log" smashes
# the "Log" variable.
#
# The array's elements are:
#
- # access_path : List of paths accessible to the child.
+ # access_path : List of paths accessible to the slave.
# access_path,norm : Ditto, in normalized form.
- # access_path,child : Ditto, as the path tokens as seen by the child.
+ # access_path,slave : Ditto, as the path tokens as seen by the slave.
# access_path,map : dict ( token -> path )
# access_path,remap : dict ( path -> token )
- # auto_path : List of paths requested by the caller as child's ::auto_path.
- # tm_path_child : List of TM root directories, as tokens seen by the child.
+ # tm_path_slave : List of TM root directories, as tokens seen by the slave.
# staticsok : Value of option -statics
# nestedok : Value of option -nested
# cleanupHook : Value of option -deleteHook
- #
- # In principle, the child can change its value of ::auto_path -
- # - a package might add a path (that is already in the access path) for
- # access to tclIndex files;
- # - the script might remove some elements of the auto_path.
- # However, this is really the business of the parent, and the auto_path will
- # be reset whenever the token mapping changes (i.e. when option -accessPath is
- # used to change the access path).
- # -autoPath is now stored in the array and is no longer obtained from
- # the child.
}
::safe::Setup