diff options
Diffstat (limited to 'library/safe.tcl')
| -rw-r--r-- | library/safe.tcl | 969 |
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 |
