summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/safe.n135
-rw-r--r--library/safe.tcl250
-rw-r--r--library/tclIndex1
-rw-r--r--tests/safe.test824
4 files changed, 1159 insertions, 51 deletions
diff --git a/doc/safe.n b/doc/safe.n
index b39f2c2..5b95eeb 100644
--- a/doc/safe.n
+++ b/doc/safe.n
@@ -23,10 +23,13 @@ safe \- Creating and manipulating safe interpreters
.sp
\fB::safe::interpFindInAccessPath\fR \fIslave\fR \fIdirectory\fR
.sp
+\fB::safe::setAutoPathSync\fR ?\fInewValue\fR?
+.sp
\fB::safe::setLogCmd\fR ?\fIcmd arg...\fR?
.SS OPTIONS
.PP
?\fB\-accessPath\fR \fIpathList\fR?
+?\fB\-autoPath\fR \fIpathList\fR?
?\fB\-statics\fR \fIboolean\fR? ?\fB\-noStatics\fR?
?\fB\-nested\fR \fIboolean\fR? ?\fB\-nestedLoadOk\fR?
?\fB\-deleteHook\fR \fIscript\fR?
@@ -140,6 +143,15 @@ $slave eval [list set tk_library \e
.CE
.RE
.TP
+\fB::safe::setAutoPathSync\fR ?\fInewValue\fR?
+This command is used to get or set the "Sync Mode" of the Safe Base.
+When an argument is supplied, the command returns an error if the argument
+is not a boolean value, or if any Safe Base interpreters exist. Typically
+the value will be set as part of initialization - boolean true for
+"Sync Mode" on (the default), false for "Sync Mode" off. With "Sync Mode"
+on, the Safe Base keeps each slave interpreter's ::auto_path synchronized
+with its access path. See the section \fBSYNC MODE\fR below for details.
+.TP
\fB::safe::setLogCmd\fR ?\fIcmd arg...\fR?
This command installs a script that will be called when interesting
life cycle events occur for a safe interpreter.
@@ -191,6 +203,13 @@ master for auto-loading.
See the section \fBSECURITY\fR below for more detail about virtual paths,
tokens and access control.
.TP
+\fB\-autoPath\fR \fIdirectoryList\fR
+This option sets the list of directories in the safe interpreter's
+::auto_path. The option is undefined if the Safe Base has "Sync Mode" on
+- in that case the safe interpreter's ::auto_path is managed by the Safe
+Base and is a tokenized form of its access path.
+See the section \fBSYNC MODE\fR below for details.
+.TP
\fB\-statics\fR \fIboolean\fR
This option specifies if the safe interpreter will be allowed
to load statically linked packages (like \fBload {} Tk\fR).
@@ -323,7 +342,8 @@ list will be assigned a token that will be set in
the slave \fBauto_path\fR and the first element of that list will be set as
the \fBtcl_library\fR for that slave.
.PP
-If the access path argument is not given or is the empty list,
+If the access path argument is not given to \fB::safe::interpCreate\fR or
+\fB::safe::interpInit\fR or is the empty list,
the default behavior is to let the slave access the same packages
as the master has access to (Or to be more precise:
only packages written in Tcl (which by definition cannot be dangerous
@@ -349,8 +369,119 @@ When the \fIaccessPath\fR is changed after the first creation or
initialization (i.e. through \fBinterpConfigure -accessPath \fR\fIlist\fR),
an \fBauto_reset\fR is automatically evaluated in the safe interpreter
to synchronize its \fBauto_index\fR with the new token list.
+.SH SYNC MODE
+Before Tcl version 8.6.x, the Safe Base kept each safe interpreter's
+::auto_path synchronized with a tokenized form of its access path.
+Limitations of Tcl 8.4 and earlier made this feature necessary. This
+definition of ::auto_path did not conform its specification in library(n)
+and pkg_mkIndex(n), but nevertheless worked perfectly well for the discovery
+and loading of packages. The introduction of Tcl modules in Tcl 8.5 added a
+large number of directories to the access path, and it is inconvenient to
+have these additional directories unnecessarily appended to the ::auto_path.
+.PP
+In order to preserve compatibility with existing code, this synchronization
+of the ::auto_path and access path ("Sync Mode" on) is still the default.
+However, the Safe Base offers the option of limiting the safe interpreter's
+::auto_path to the much shorter list of directories that is necessary for
+it to perform its function ("Sync Mode" off). Use the command
+\fB::safe::setAutoPathSync\fR to choose the mode before creating any Safe
+Base interpreters.
+.PP
+In either mode, the most convenient way to initialize a safe interpreter is
+to call \fB::safe::interpCreate\fR or \fB::safe::interpInit\fR without the
+\fB\-accessPath\fR or \fB\-autoPath\fR options (or with the \fB\-accessPath\fR
+option set to the
+empty list), which will give the safe interpreter the same access as the
+master interpreter to packages, modules, and autoloader files. With
+"Sync Mode" off, the ::auto_path will be set to a tokenized form of the master's
+::auto_path.
+.PP
+With "Sync Mode" off, if a value is specified for \fB\-autoPath\fR, even the empty
+list, in a call to \fB::safe::interpCreate\fR, \fB::safe::interpInit\fR, or
+\fB::safe::interpConfigure\fR, it will be tokenized and used as the safe
+interpreter's ::auto_path. Any directories that do not also belong to the
+access path cannot be tokenized and will be silently ignored.
+.PP
+With "Sync Mode" off, if the access path is reset to the values in the
+master interpreter by calling \fB::safe::interpConfigure\fR with arguments
+\fB\-accessPath\fR {}, then the ::auto_path will also be reset unless the argument
+\fB\-autoPath\fR is supplied to specify a different value.
+.PP
+With "Sync Mode" off, if a non-empty value of \fB\-accessPath\fR is supplied, the
+safe interpreter's ::auto_path will be set to {} (by
+\fB::safe::interpCreate\fR, \fB::safe::interpInit\fR) or left unchanged
+(by \fB::safe::interpConfigure\fR). If the same command specifies a new
+value for \fB\-autoPath\fR, it will be applied after the \fB\-accessPath\fR argument has
+been processed.
+
+Examples of use with "Sync Mode" off: any of these commands will set the
+::auto_path to a tokenized form of its value in the master interpreter:
+.RS
+.PP
+.CS
+ safe::interpCreate foo
+ safe::interpCreate foo -accessPath {}
+ safe::interpInit bar
+ safe::interpInit bar -accessPath {}
+ safe::interpConfigure foo -accessPath {}
+.CE
+.RE
+.TP
+Example of use with "Sync Mode" off: when initializing a safe interpreter
+with a non-empty access path, the ::auto_path will be set to {} unless its
+own value is also specified:
+.RS
+.PP
+.CS
+ safe::interpCreate foo -accessPath {
+ /usr/local/TclHome/lib/tcl8.6
+ /usr/local/TclHome/lib/tcl8.6/http1.0
+ /usr/local/TclHome/lib/tcl8.6/opt0.4
+ /usr/local/TclHome/lib/tcl8.6/msgs
+ /usr/local/TclHome/lib/tcl8.6/encoding
+ /usr/local/TclHome/lib
+ }
+
+ # The slave's ::auto_path must be given a suitable value:
+
+ safe::interpConfigure foo -autoPath {
+ /usr/local/TclHome/lib/tcl8.6
+ /usr/local/TclHome/lib
+ }
+
+ # The two commands can be combined:
+
+ safe::interpCreate foo -accessPath {
+ /usr/local/TclHome/lib/tcl8.6
+ /usr/local/TclHome/lib/tcl8.6/http1.0
+ /usr/local/TclHome/lib/tcl8.6/opt0.4
+ /usr/local/TclHome/lib/tcl8.6/msgs
+ /usr/local/TclHome/lib/tcl8.6/encoding
+ /usr/local/TclHome/lib
+ } -autoPath {
+ /usr/local/TclHome/lib/tcl8.6
+ /usr/local/TclHome/lib
+ }
+.CE
+.RE
+.TP
+Example of use with "Sync Mode" off: the command
+\fBsafe::interpAddToAccessPath\fR does not change the safe interpreter's
+::auto_path, and so any necessary change must be made by the script:
+.RS
+.PP
+.CS
+ safe::interpAddToAccessPath foo /usr/local/TclHome/lib/extras/Img1.4.11
+
+ lassign [safe::interpConfigure foo -autoPath] DUM slaveAutoPath
+ lappend slaveAutoPath /usr/local/TclHome/lib/extras/Img1.4.11
+ safe::interpConfigure foo -autoPath $slaveAutoPath
+.CE
+.RE
+.TP
.SH "SEE ALSO"
-interp(n), library(n), load(n), package(n), source(n), unknown(n)
+interp(n), library(n), load(n), package(n), pkg_mkIndex(n), source(n),
+tm(n), unknown(n)
.SH KEYWORDS
alias, auto\-loading, auto_mkindex, load, master interpreter, safe
interpreter, slave interpreter, source
diff --git a/library/safe.tcl b/library/safe.tcl
index e3eabac..9218380 100644
--- a/library/safe.tcl
+++ b/library/safe.tcl
@@ -78,18 +78,29 @@ 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]
+
+ set withAutoPath [::tcl::OptProcArgGiven -autoPath]
InterpCreate $slave $accessPath \
- [InterpStatics] [InterpNested] $deleteHook
+ [InterpStatics] [InterpNested] $deleteHook $autoPath $withAutoPath
}
proc ::safe::interpInit {args} {
+ variable AutoPathSync
+ if {$AutoPathSync} {
+ set autoPath {}
+ }
set Args [::tcl::OptKeyParse ::safe::interpIC $args]
if {![::interp exists $slave]} {
return -code error "\"$slave\" is not an interpreter"
}
+ set withAutoPath [::tcl::OptProcArgGiven -autoPath]
InterpInit $slave $accessPath \
- [InterpStatics] [InterpNested] $deleteHook
+ [InterpStatics] [InterpNested] $deleteHook $autoPath $withAutoPath
}
# Check that the given slave is "one of us"
@@ -115,6 +126,7 @@ proc ::safe::CheckInterp {slave} {
# 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
@@ -125,11 +137,17 @@ proc ::safe::interpConfigure {args} {
CheckInterp $slave
namespace upvar ::safe S$slave state
- return [join [list \
+ set TMP [list \
[list -accessPath $state(access_path)] \
[list -statics $state(staticsok)] \
[list -nested $state(nestedok)] \
- [list -deleteHook $state(cleanupHook)]]]
+ [list -deleteHook $state(cleanupHook)] \
+ ]
+ if {!$AutoPathSync} {
+ set SLAP [DetokPath $slave [$slave eval set ::auto_path]]
+ lappend TMP [list -autoPath $SLAP]
+ }
+ return [join $TMP]
}
2 {
# If we have exactly 2 arguments the semantic is a "configure
@@ -154,6 +172,14 @@ proc ::safe::interpConfigure {args} {
-accessPath {
return [list -accessPath $state(access_path)]
}
+ -autoPath {
+ if {$AutoPathSync} {
+ return -code error "unknown flag $name (bug)"
+ } else {
+ set SLAP [DetokPath $slave [$slave eval set ::auto_path]]
+ return [list -autoPath $SLAP]
+ }
+ }
-statics {
return [list -statics $state(staticsok)]
}
@@ -197,6 +223,13 @@ proc ::safe::interpConfigure {args} {
} else {
set doreset 1
}
+ if {(!$AutoPathSync) && (![::tcl::OptProcArgGiven -autoPath])} {
+ set SLAP [DetokPath $slave [$slave eval set ::auto_path]]
+ set autoPath $SLAP
+ } elseif {$AutoPathSync} {
+ set autoPath {}
+ } else {
+ }
if {
![::tcl::OptProcArgGiven -statics]
&& ![::tcl::OptProcArgGiven -noStatics]
@@ -217,8 +250,10 @@ proc ::safe::interpConfigure {args} {
set deleteHook $state(cleanupHook)
}
# we can now reconfigure :
- set slave_tm_rel [InterpSetConfig $slave $accessPath $statics $nested $deleteHook]
- # auto_reset the slave (to completly synch the new access_path)
+ set withAutoPath [::tcl::OptProcArgGiven -autoPath]
+ set slave_tm_rel [InterpSetConfig $slave $accessPath $statics $nested $deleteHook $autoPath $withAutoPath]
+
+ # auto_reset the slave (to completely synch the new access_path) tests safe-9.8 safe-9.9
if {$doreset} {
if {[catch {::interp eval $slave {auto_reset}} msg]} {
Log $slave "auto_reset failed: $msg"
@@ -269,10 +304,11 @@ proc ::safe::interpConfigure {args} {
# Optional Arguments :
# + slave name : if empty, generated name will be used
# + access_path: path list controlling where load/source can occur,
-# if empty: the master auto_path will be used.
+# if empty: the master auto_path and its subdirectories 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
@@ -282,6 +318,8 @@ proc ::safe::InterpCreate {
staticsok
nestedok
deletehook
+ autoPath
+ withAutoPath
} {
# Create the slave.
if {$slave ne ""} {
@@ -293,20 +331,25 @@ proc ::safe::InterpCreate {
Log $slave "Created" NOTICE
# Initialize it. (returns slave name)
- InterpInit $slave $access_path $staticsok $nestedok $deletehook
+ InterpInit $slave $access_path $staticsok $nestedok $deletehook $autoPath $withAutoPath
}
#
# InterpSetConfig (was setAccessPath) :
-# Sets up slave virtual auto_path and corresponding structure within
+# Sets up slave virtual access 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 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 ![setAutoPathSync]), to set the slave's ::auto_path.
-proc ::safe::InterpSetConfig {slave access_path staticsok nestedok deletehook} {
+proc ::safe::InterpSetConfig {slave access_path staticsok nestedok deletehook autoPath withAutoPath} {
global auto_path
+ variable AutoPathSync
# determine and store the access path if empty
if {$access_path eq ""} {
@@ -329,14 +372,25 @@ proc ::safe::InterpSetConfig {slave access_path staticsok nestedok deletehook} {
moved it to front of slave's access_path" NOTICE
}
+ set raw_auto_path $access_path
+
# 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 $slave "Setting accessPath=($access_path) staticsok=$staticsok\
nestedok=$nestedok deletehook=($deletehook)" NOTICE
+ if {!$AutoPathSync} {
+ Log $slave "Setting auto_path=($raw_auto_path)" NOTICE
+ }
namespace upvar ::safe S$slave state
@@ -346,7 +400,6 @@ proc ::safe::InterpSetConfig {slave access_path staticsok nestedok deletehook} {
# We save the virtual form separately as well, as syncing it with the
# slave has to be defered until the necessary commands are present for
# setup.
-
set norm_access_path {}
set slave_access_path {}
set map_access_path {}
@@ -365,6 +418,20 @@ proc ::safe::InterpSetConfig {slave access_path staticsok nestedok deletehook} {
incr i
}
+ # Set the slave auto_path to a tokenized raw_auto_path.
+ # Silently ignore any directories that are not in the access path.
+ # If [setAutoPathSync], SyncAccessPath will overwrite this value with the
+ # full access path.
+ # If ![setAutoPathSync], 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 $slave [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]} {
@@ -430,14 +497,38 @@ proc ::safe::InterpSetConfig {slave access_path staticsok nestedok deletehook} {
set state(cleanupHook) $deletehook
SyncAccessPath $slave
+
return $slave_tm_rel
}
+
+#
+# DetokPath:
+# Convert tokens to directories where possible.
+# Leave undefined tokens unconverted. They are
+# nonsense in both the slave and the master.
#
+proc ::safe::DetokPath {slave tokenPath} {
+ namespace upvar ::safe S$slave state
+
+ set slavePath {}
+ foreach token $tokenPath {
+ if {[dict exists $state(access_path,map) $token]} {
+ lappend slavePath [dict get $state(access_path,map) $token]
+ } else {
+ lappend slavePath $token
+ }
+ }
+ return $slavePath
+}
+
#
-# FindInAccessPath:
+#
+# interpFindInAccessPath:
# Search for a real directory and returns its virtual Id (including the
# "$")
+#
+# When debugging, use TranslatePath for the inverse operation.
proc ::safe::interpFindInAccessPath {slave path} {
namespace upvar ::safe S$slave state
@@ -448,6 +539,7 @@ proc ::safe::interpFindInAccessPath {slave path} {
return [dict get $state(access_path,remap) $path]
}
+
#
# addToAccessPath:
# add (if needed) a real directory to access path and return its
@@ -483,9 +575,11 @@ proc ::safe::InterpInit {
staticsok
nestedok
deletehook
+ autoPath
+ withAutoPath
} {
# Configure will generate an access_path when access_path is empty.
- InterpSetConfig $slave $access_path $staticsok $nestedok $deletehook
+ InterpSetConfig $slave $access_path $staticsok $nestedok $deletehook $autoPath $withAutoPath
# NB we need to add [namespace current], aliases are always absolute
# paths.
@@ -591,6 +685,15 @@ proc ::safe::interpDelete {slave} {
namespace upvar ::safe S$slave state
+ # Sub interpreters would be deleted automatically, but if they are managed
+ # by the Safe Base we also need to clean up, and this needs to be done
+ # independently of the cleanupHook.
+ foreach sub [interp slaves $slave] {
+ if {[info exists ::safe::S[list $slave $sub]]} {
+ ::safe::interpDelete [list $slave $sub]
+ }
+ }
+
# 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
@@ -657,24 +760,28 @@ proc ::safe::setLogCmd {args} {
# ------------------- END OF PUBLIC METHODS ------------
#
-# Sets the slave auto_path to the master recorded value. Also sets
-# tcl_library to the first token of the virtual path.
+# Sets the slave auto_path to its recorded access path. Also sets
+# tcl_library to the first token of the access path.
#
proc ::safe::SyncAccessPath {slave} {
+ variable AutoPathSync
namespace upvar ::safe S$slave state
set slave_access_path $state(access_path,slave)
- ::interp eval $slave [list set auto_path $slave_access_path]
+ if {$AutoPathSync} {
+ ::interp eval $slave [list set auto_path $slave_access_path]
- Log $slave "auto_path in $slave has been set to $slave_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 auto_path's. See -> InterpSetConfig for the code which
+ # list of access path's. See -> InterpSetConfig for the code which
# ensures this condition.
::interp eval $slave [list \
set tcl_library [lindex $slave_access_path 0]]
+ return
}
# Returns the virtual token for directory number N.
@@ -734,6 +841,7 @@ proc ::safe::AliasFileSubcommand {slave subcommand name} {
# AliasGlob is the target of the "glob" alias in safe interpreters.
proc ::safe::AliasGlob {slave args} {
+ variable AutoPathSync
Log $slave "GLOB ! $args" NOTICE
set cmd {}
set at 0
@@ -756,11 +864,15 @@ proc ::safe::AliasGlob {slave args} {
while {$at < [llength $args]} {
switch -glob -- [set opt [lindex $args $at]] {
- -nocomplain - -- - -join - -tails {
+ -nocomplain - -- - -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
@@ -775,15 +887,20 @@ proc ::safe::AliasGlob {slave args} {
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"
+ if {$AutoPathSync} {
+ # 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"
+ } else {
+ break
+ }
}
-* {
Log $slave "Safe base rejecting glob option '$opt'"
return -code error "Safe base rejecting glob option '$opt'"
+ # unsafe/unnecessary options rejected: -path
}
default {
break
@@ -807,7 +924,7 @@ proc ::safe::AliasGlob {slave args} {
lappend cmd -directory $dir
}
- # Apply the -join semantics ourselves
+ # Apply the -join semantics ourselves (hence -join not copied to $cmd)
if {$got(-join)} {
set args [lreplace $args $at end [join [lrange $args $at end] "/"]]
}
@@ -1100,16 +1217,21 @@ proc ::safe::Setup {} {
# Setup the arguments parsing
#
####
+ variable AutoPathSync
# Share the descriptions
- set temp [::tcl::OptKeyRegister {
+ set OptList {
{-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 slave"}
+ }
+ set temp [::tcl::OptKeyRegister $OptList]
# create case (slave is optional)
::tcl::OptKeyRegister {
@@ -1146,8 +1268,72 @@ proc ::safe::Setup {} {
return
}
+# Accessor method for ::safe::AutoPathSync
+# Usage: ::safe::setAutoPathSync ?newValue?
+# Respond to changes by calling Setup again, precerving 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::setAutoPathSync {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 slaves 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::setAutoPathSync ?newValue?"}
+ return -code error $msg
+ }
+
+ return $AutoPathSync
+}
+
namespace eval ::safe {
- # internal variables
+ # internal variables (must not begin with "S")
+
+ # AutoPathSync
+ #
+ # Set AutoPathSync to 0 to give a slave'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 slave's auto_path. If these commands are
+ # called with no (or empty) access path, the slave's auto_path will be set
+ # to a tokenized form of the master'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 slave'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
# Log command, set via 'setLogCmd'. Logging is disabled when empty.
variable Log {}
@@ -1169,6 +1355,10 @@ namespace eval ::safe {
# staticsok : Value of option -statics
# nestedok : Value of option -nested
# cleanupHook : Value of option -deleteHook
+ #
+ # Because the slave can change its value of ::auto_path, the value of
+ # option -autoPath is not stored in the array but must be obtained from
+ # the slave.
}
::safe::Setup
diff --git a/library/tclIndex b/library/tclIndex
index 87a2814..6bb3fa6 100644
--- a/library/tclIndex
+++ b/library/tclIndex
@@ -61,6 +61,7 @@ set auto_index(::safe::DirInAccessPath) [list ::tcl::Pkg::source [file join $dir
set auto_index(::safe::Subset) [list ::tcl::Pkg::source [file join $dir safe.tcl]]
set auto_index(::safe::AliasSubset) [list ::tcl::Pkg::source [file join $dir safe.tcl]]
set auto_index(::safe::AliasEncoding) [list ::tcl::Pkg::source [file join $dir safe.tcl]]
+set auto_index(::safe::setAutoPathSync) [list source [file join $dir safe.tcl]]
set auto_index(tcl_wordBreakAfter) [list ::tcl::Pkg::source [file join $dir word.tcl]]
set auto_index(tcl_wordBreakBefore) [list ::tcl::Pkg::source [file join $dir word.tcl]]
set auto_index(tcl_endOfWord) [list ::tcl::Pkg::source [file join $dir word.tcl]]
diff --git a/tests/safe.test b/tests/safe.test
index 6838201..c01ce47 100644
--- a/tests/safe.test
+++ b/tests/safe.test
@@ -17,6 +17,8 @@ if {[lsearch [namespace children] ::tcltest] == -1} {
namespace import -force ::tcltest::*
}
+testConstraint AutoSyncDefined 1
+
foreach i [interp slaves] {
interp delete $i
}
@@ -28,20 +30,20 @@ set ::auto_path [info library]
# - Replaced here with tests using example packages provided in subdirectory
# auto0 of the tests directory, which are independent of any changes
# made to the packages provided with Tcl.
-# - These are tests 7.1 7.2 7.4 9.10 9.12
+# - These are tests 7.1 7.2 7.4 9.10 9.12 18.1 18.2 18.4
# - Tests 7.0[a-f] test the example packages themselves before they
# are used to test Safe Base interpreters.
# - Alternatively use packages opt and (from cookiejar) tcl::idna.
# - These alternative tests have suffix "opt".
-# - These are 7.[124]opt, 9.1[02]opt
-# - Tests 7.[124]opt, 9.1[02]opt use "package require opt".
+# - These are 7.[124]opt, 9.1[02]opt, 18.[124]opt.
+# - Tests 7.[124]opt, 9.1[02]opt, 18.[124]opt use "package require opt".
# - Tests 9.1[02]opt also use "package require tcl::idna".
#
# When using package opt for testing positive/negative package search:
# - The directory location and the error message depend on whether
# and how the package is installed.
-# Error message for tests 7.2opt for "package require opt".
+# Error message for tests 7.2opt, 18.2opt for "package require opt".
if {[string match *zipfs:/* [info library]]} {
# pkgIndex.tcl is in [info library]
# file to be sourced is in [info library]/opt*
@@ -52,7 +54,7 @@ if {[string match *zipfs:/* [info library]]} {
set pkgOptErrMsg {can't find package opt}
}
-# Directory of opt for tests 7.4opt, 9.10opt, 9.12opt
+# Directory of opt for tests 7.4opt, 9.10opt, 9.12opt, 18.4opt
# for "package require opt".
if {[file exists [file join [info library] opt0.4]]} {
# Installed files in lib8.7/opt0.4
@@ -80,7 +82,7 @@ set TestsDir [file normalize [file dirname [info script]]]
set ZipMountPoint [zipfs root]auto-files
zipfs mount $ZipMountPoint [file join $TestsDir auto-files.zip]
-# Force actual loading of the safe package because we use un exported (and
+# Force actual loading of the safe package because we use un-exported (and
# thus un-autoindexed) APIs in this test result arguments:
catch {safe::interpConfigure}
@@ -463,8 +465,16 @@ test safe-7.0fz {example modules packages, test in master interpreter, append to
} -match glob -result {0 0.5 0 1.0 0 2.0 -- res0 res1 res2}
# high level general test
-# Use example packages not http1.0
-test safe-7.1 {tests that everything works at high level} -setup {
+# Use example packages not tcl8.x/opt
+test safe-7.1 {tests that everything works at high level with conventional AutoPathSync} -setup {
+ # All ::safe commands are loaded at start of file.
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ }
+
set tmpAutoPath $::auto_path
lappend ::auto_path [file join $TestsDir auto0]
set i [safe::interpCreate]
@@ -479,10 +489,21 @@ test safe-7.1 {tests that everything works at high level} -setup {
set v
} -cleanup {
safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result 1.2.3
# high level general test
# Use zipped example packages not tcl8.x/opt
-test safe-7.1z {tests that everything works at high level; zipfs} -setup {
+test safe-7.1z {tests that everything works at high level with conventional AutoPathSync; zipfs} -setup {
+ # All ::safe commands are loaded at start of file.
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ }
+
set tmpAutoPath $::auto_path
lappend ::auto_path [file join $TestsDir auto0]
set i [safe::interpCreate]
@@ -497,9 +518,20 @@ test safe-7.1z {tests that everything works at high level; zipfs} -setup {
set v
} -cleanup {
safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result 1.2.3
# high level general test
-test safe-7.1opt {tests that everything works at high level, uses pkg opt} -setup {
+test safe-7.1opt {tests that everything works at high level with conventional AutoPathSync, use pkg opt} -setup {
+ # All ::safe commands are loaded at start of file.
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ }
+
set i [safe::interpCreate]
} -body {
# no error shall occur:
@@ -511,8 +543,20 @@ test safe-7.1opt {tests that everything works at high level, uses pkg opt} -setu
set v
} -cleanup {
safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result 0.4.*
-test safe-7.2 {tests specific path and interpFind/AddToAccessPath} -setup {
+test safe-7.2 {tests specific path and interpFind/AddToAccessPath with conventional AutoPathSync} -setup {
+ # All ::safe commands are loaded at start of file.
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ } else {
+ set SyncVal_TMP 1
+ }
} -body {
set i [safe::interpCreate -nostat -nested 1 -accessPath [list [info library]]]
# should not add anything (p0)
@@ -528,11 +572,23 @@ test safe-7.2 {tests specific path and interpFind/AddToAccessPath} -setup {
[safe::interpConfigure $i]\
[safe::interpDelete $i]
} -cleanup {
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:0:)} {\$p(:*:)} {\$p(:*:)} 1\
{can't find package SafeTestPackage1}\
{-accessPath {[list $tcl_library */dummy/unixlike/test/path $TestsDir/auto0]}\
-statics 0 -nested 1 -deleteHook {}} {}"
-test safe-7.2z {tests specific path and interpFind/AddToAccessPath; zipfs} -setup {
+test safe-7.2z {tests specific path and interpFind/AddToAccessPath with conventional AutoPathSync; zipfs} -setup {
+ # All ::safe commands are loaded at start of file.
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ } else {
+ set SyncVal_TMP 1
+ }
} -body {
set i [safe::interpCreate -nostat -nested 1 -accessPath [list [info library]]]
# should not add anything (p0)
@@ -548,11 +604,23 @@ test safe-7.2z {tests specific path and interpFind/AddToAccessPath; zipfs} -setu
[safe::interpConfigure $i]\
[safe::interpDelete $i]
} -cleanup {
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:0:)} {\$p(:*:)} {\$p(:*:)} 1\
{can't find package SafeTestPackage1}\
{-accessPath {[list $tcl_library */dummy/unixlike/test/path $TestsDir/auto0]}\
-statics 0 -nested 1 -deleteHook {}} {}"
-test safe-7.2opt {tests specific path and interpFind/AddToAccessPath, uses pkg opt} -setup {
+test safe-7.2opt {tests specific path and interpFind/AddToAccessPath with conventional AutoPathSync, use pkg opt} -setup {
+ # All ::safe commands are loaded at start of file.
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ } else {
+ set SyncVal_TMP 1
+ }
} -body {
set i [safe::interpCreate -nostat -nested 1 -accessPath [list [info library]]]
# should not add anything (p0)
@@ -566,6 +634,9 @@ test safe-7.2opt {tests specific path and interpFind/AddToAccessPath, uses pkg o
[safe::interpConfigure $i]\
[safe::interpDelete $i]
} -cleanup {
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:0:)} {\$p(:*:)} 1 {$pkgOptErrMsg}\
{-accessPath {[list $tcl_library */dummy/unixlike/test/path]}\
-statics 0 -nested 1 -deleteHook {}} {}"
@@ -574,7 +645,16 @@ test safe-7.3 {check that safe subinterpreters work} {
set j [safe::interpCreate [list $i x]]
list [interp eval $j {join {o k} ""}] [safe::interpDelete $i] [interp exists $j]
} {ok {} 0}
-test safe-7.4 {tests specific path and positive search} -setup {
+test safe-7.4 {tests specific path and positive search with conventional AutoPathSync} -setup {
+ # All ::safe commands are loaded at start of file.
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ } else {
+ set SyncVal_TMP 1
+ }
} -body {
set i [safe::interpCreate -nostat -nested 1 -accessPath [list [info library]]]
# should not add anything (p0)
@@ -589,10 +669,22 @@ test safe-7.4 {tests specific path and positive search} -setup {
# Note that the glob match elides directories (those from the module path)
# other than the first and last in the access path.
} -cleanup {
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:0:)} {\$p(:*:)} 0 1.2.3\
{-accessPath {[list $tcl_library * $TestsDir/auto0/auto1]}\
-statics 0 -nested 1 -deleteHook {}} {}"
-test safe-7.4z {tests specific path and positive search; zipfs} -setup {
+test safe-7.4z {tests specific path and positive search with conventional AutoPathSync; zipfs} -setup {
+ # All ::safe commands are loaded at start of file.
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ } else {
+ set SyncVal_TMP 1
+ }
} -body {
set i [safe::interpCreate -nostat -nested 1 -accessPath [list [info library]]]
# should not add anything (p0)
@@ -607,10 +699,22 @@ test safe-7.4z {tests specific path and positive search; zipfs} -setup {
# Note that the glob match elides directories (those from the module path)
# other than the first and last in the access path.
} -cleanup {
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:0:)} {\$p(:*:)} 0 1.2.3\
{-accessPath {[list $tcl_library * $TestsDir/auto0/auto1]}\
-statics 0 -nested 1 -deleteHook {}} {}"
-test safe-7.4opt {tests specific path and positive search, uses pkg opt} -setup {
+test safe-7.4opt {tests specific path and positive search with conventional AutoPathSync, use pkg opt} -setup {
+ # All ::safe commands are loaded at start of file.
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ } else {
+ set SyncVal_TMP 1
+ }
} -body {
set i [safe::interpCreate -nostat -nested 1 -accessPath [list [info library]]]
# should not add anything (p0)
@@ -625,10 +729,42 @@ test safe-7.4opt {tests specific path and positive search, uses pkg opt} -setup
# Note that the glob match elides directories (those from the module path)
# other than the first and last in the access path.
} -cleanup {
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:0:)} {\$p(:*:)} 0 0.4.*\
{-accessPath {[list $tcl_library *$tcl_library/$pkgOptDir]}\
-statics 0 -nested 1 -deleteHook {}} {}"
+test safe-7.5 {tests positive and negative module loading with conventional AutoPathSync} -setup {
+ # All ::safe commands are loaded at start of file.
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ }
+
+ set i [safe::interpCreate]
+
+ interp eval $i {
+ package forget platform::shell
+ package forget platform
+ catch {namespace delete ::platform}
+ }
+} -body {
+ # Should raise an error (module ancestor directory issue)
+ set code1 [catch {interp eval $i {package require shell}} msg1]
+ # Should not raise an error
+ set code2 [catch {interp eval $i {package require platform::shell}} msg2]
+ return [list $code1 $msg1 $code2]
+} -cleanup {
+ safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
+} -result {1 {can't find package shell} 0}
+
# test source control on file name
set i "a"
test safe-8.1 {safe source control on file} -setup {
@@ -822,8 +958,9 @@ test safe-9.6 {interpConfigure widget like behaviour} -body {
safe::interpConfigure $i]\
[safe::interpConfigure $i -deleteHook toto -nosta -nested 0
safe::interpConfigure $i]
+} -cleanup {
+ safe::interpDelete $i
} -match glob -result {{-accessPath * -statics 0 -nested 1 -deleteHook {foo bar}} {-accessPath *} {-nested 1} {-statics 0} {-deleteHook {foo bar}} {-accessPath * -statics 1 -nested 1 -deleteHook {foo bar}} {-accessPath * -statics 0 -nested 0 -deleteHook toto}}
-
test safe-9.7 {interpConfigure widget like behaviour (demystified)} -body {
# this test shall work, believed equivalent to 9.6
set i [safe::interpCreate \
@@ -847,8 +984,12 @@ test safe-9.7 {interpConfigure widget like behaviour (demystified)} -body {
} -cleanup {
safe::interpDelete $i
} -match glob -result {{-accessPath * -statics 0 -nested 1 -deleteHook {foo bar}} {-accessPath *} {-nested 1} {-statics 0} {-deleteHook {foo bar}} {-accessPath * -statics 1 -nested 1 -deleteHook {foo bar}} {-accessPath * -statics 0 -nested 0 -deleteHook toto}}
-
test safe-9.8 {interpConfigure change the access path; tclIndex commands unaffected by token rearrangement (dummy test of doreset)} -setup {
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ }
} -body {
set i [safe::interpCreate -accessPath [list $tcl_library \
[file join $TestsDir auto0 auto1] \
@@ -884,12 +1025,20 @@ test safe-9.8 {interpConfigure change the access path; tclIndex commands unaffec
list $path1 $path2 $path3 $path4 $code3 $msg3 $code4 $msg4 $confA $confB
} -cleanup {
safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:1:)} {\$p(:2:)} {\$p(:2:)} {\$p(:1:)} 0 ok1 0 ok2\
{-accessPath {[list $tcl_library $TestsDir/auto0/auto1 $TestsDir/auto0/auto2]*}\
-statics 1 -nested 0 -deleteHook {}}\
{-accessPath {[list $tcl_library $TestsDir/auto0/auto2 $TestsDir/auto0/auto1]*}\
-statics 1 -nested 0 -deleteHook {}}"
test safe-9.8z {interpConfigure change the access path; tclIndex commands unaffected by token rearrangement (dummy test of doreset); zipfs} -setup {
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ }
} -body {
set i [safe::interpCreate -accessPath [list $tcl_library \
[file join $ZipMountPoint auto0 auto1] \
@@ -925,12 +1074,20 @@ test safe-9.8z {interpConfigure change the access path; tclIndex commands unaffe
list $path1 $path2 $path3 $path4 $code3 $msg3 $code4 $msg4 $confA $confB
} -cleanup {
safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:1:)} {\$p(:2:)} {\$p(:2:)} {\$p(:1:)} 0 ok1 0 ok2\
{-accessPath {[list $tcl_library $ZipMountPoint/auto0/auto1 $ZipMountPoint/auto0/auto2]*}\
-statics 1 -nested 0 -deleteHook {}}\
{-accessPath {[list $tcl_library $ZipMountPoint/auto0/auto2 $ZipMountPoint/auto0/auto1]*}\
-statics 1 -nested 0 -deleteHook {}}"
test safe-9.9 {interpConfigure change the access path; tclIndex commands unaffected by token rearrangement (actual test of doreset)} -setup {
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ }
} -body {
set i [safe::interpCreate -accessPath [list $tcl_library \
[file join $TestsDir auto0 auto1] \
@@ -964,12 +1121,20 @@ test safe-9.9 {interpConfigure change the access path; tclIndex commands unaffec
list $path1 $path2 $path3 $path4 $code3 $msg3 $code4 $msg4 $confA $confB
} -cleanup {
safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:1:)} {\$p(:2:)} {\$p(:2:)} {\$p(:1:)} 0 ok1 0 ok2\
{-accessPath {[list $tcl_library $TestsDir/auto0/auto1 $TestsDir/auto0/auto2]*}\
-statics 1 -nested 0 -deleteHook {}}\
{-accessPath {[list $tcl_library $TestsDir/auto0/auto2 $TestsDir/auto0/auto1]*}\
-statics 1 -nested 0 -deleteHook {}}"
test safe-9.9z {interpConfigure change the access path; tclIndex commands unaffected by token rearrangement (actual test of doreset); zipfs} -setup {
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ }
} -body {
set i [safe::interpCreate -accessPath [list $tcl_library \
[file join $ZipMountPoint auto0 auto1] \
@@ -1003,12 +1168,20 @@ test safe-9.9z {interpConfigure change the access path; tclIndex commands unaffe
list $path1 $path2 $path3 $path4 $code3 $msg3 $code4 $msg4 $confA $confB
} -cleanup {
safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:1:)} {\$p(:2:)} {\$p(:2:)} {\$p(:1:)} 0 ok1 0 ok2\
{-accessPath {[list $tcl_library $ZipMountPoint/auto0/auto1 $ZipMountPoint/auto0/auto2]*}\
-statics 1 -nested 0 -deleteHook {}}\
{-accessPath {[list $tcl_library $ZipMountPoint/auto0/auto2 $ZipMountPoint/auto0/auto1]*}\
-statics 1 -nested 0 -deleteHook {}}"
test safe-9.10 {interpConfigure change the access path; pkgIndex.tcl packages unaffected by token rearrangement} -setup {
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ }
} -body {
# For complete correspondence to safe-9.10opt, include auto0 in access path.
set i [safe::interpCreate -accessPath [list $tcl_library \
@@ -1049,6 +1222,9 @@ test safe-9.10 {interpConfigure change the access path; pkgIndex.tcl packages un
} -cleanup {
safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:2:)} {\$p(:3:)} {\$p(:3:)} {\$p(:2:)} 0 1.2.3 0 2.3.4\
{-accessPath {[list $tcl_library $TestsDir/auto0 $TestsDir/auto0/auto1 $TestsDir/auto0/auto2]*}\
-statics 1 -nested 0 -deleteHook {}}\
@@ -1056,6 +1232,11 @@ test safe-9.10 {interpConfigure change the access path; pkgIndex.tcl packages un
-statics 1 -nested 0 -deleteHook {}}\
0 OK1 0 OK2"
test safe-9.10z {interpConfigure change the access path; pkgIndex.tcl packages unaffected by token rearrangement; zipfs} -setup {
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ }
} -body {
# For complete correspondence to safe-9.10opt, include auto0 in access path.
set i [safe::interpCreate -accessPath [list $tcl_library \
@@ -1096,6 +1277,9 @@ test safe-9.10z {interpConfigure change the access path; pkgIndex.tcl packages u
} -cleanup {
safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:2:)} {\$p(:3:)} {\$p(:3:)} {\$p(:2:)} 0 1.2.3 0 2.3.4\
{-accessPath {[list $tcl_library $ZipMountPoint/auto0 $ZipMountPoint/auto0/auto1 $ZipMountPoint/auto0/auto2]*}\
-statics 1 -nested 0 -deleteHook {}}\
@@ -1103,6 +1287,11 @@ test safe-9.10z {interpConfigure change the access path; pkgIndex.tcl packages u
-statics 1 -nested 0 -deleteHook {}}\
0 OK1 0 OK2"
test safe-9.10opt {interpConfigure change the access path; pkgIndex.tcl packages unaffected by token rearrangement, uses pkg opt and tcl::idna} -setup {
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ }
} -body {
set i [safe::interpCreate -accessPath [list $tcl_library \
[file join $tcl_library $pkgOptDir] \
@@ -1137,12 +1326,20 @@ test safe-9.10opt {interpConfigure change the access path; pkgIndex.tcl packages
$confA $confB $code5 $msg5 $code6 $msg6
} -cleanup {
safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:1:)} {\$p(:2:)} {\$p(:2:)} {\$p(:1:)} 0 1.* 0 0.4.*\
{-accessPath {[list $tcl_library $tcl_library/$pkgOptDir $tcl_library/$pkgJarDir]*}\
-statics 1 -nested 0 -deleteHook {}}\
{-accessPath {[list $tcl_library $tcl_library/$pkgJarDir $tcl_library/$pkgOptDir]*}\
-statics 1 -nested 0 -deleteHook {}} 0 0 0 example.com"
test safe-9.11 {interpConfigure change the access path; pkgIndex.tcl packages unaffected by token rearrangement, 9.10 without path auto0} -setup {
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ }
} -body {
set i [safe::interpCreate -accessPath [list $tcl_library \
[file join $TestsDir auto0 auto1] \
@@ -1176,6 +1373,9 @@ test safe-9.11 {interpConfigure change the access path; pkgIndex.tcl packages un
$code5 $msg5 $code6 $msg6
} -cleanup {
safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:1:)} {\$p(:2:)} {\$p(:2:)} {\$p(:1:)} 0 1.2.3 0 2.3.4\
{-accessPath {[list $tcl_library $TestsDir/auto0/auto1 $TestsDir/auto0/auto2]*}\
-statics 1 -nested 0 -deleteHook {}}\
@@ -1183,6 +1383,11 @@ test safe-9.11 {interpConfigure change the access path; pkgIndex.tcl packages un
-statics 1 -nested 0 -deleteHook {}}\
0 OK1 0 OK2"
test safe-9.11z {interpConfigure change the access path; pkgIndex.tcl packages unaffected by token rearrangement, 9.10 without path auto0; zipfs} -setup {
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ }
} -body {
set i [safe::interpCreate -accessPath [list $tcl_library \
[file join $ZipMountPoint auto0 auto1] \
@@ -1216,6 +1421,9 @@ test safe-9.11z {interpConfigure change the access path; pkgIndex.tcl packages u
$code5 $msg5 $code6 $msg6
} -cleanup {
safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:1:)} {\$p(:2:)} {\$p(:2:)} {\$p(:1:)} 0 1.2.3 0 2.3.4\
{-accessPath {[list $tcl_library $ZipMountPoint/auto0/auto1 $ZipMountPoint/auto0/auto2]*}\
-statics 1 -nested 0 -deleteHook {}}\
@@ -1223,6 +1431,11 @@ test safe-9.11z {interpConfigure change the access path; pkgIndex.tcl packages u
-statics 1 -nested 0 -deleteHook {}}\
0 OK1 0 OK2"
test safe-9.12 {interpConfigure change the access path; pkgIndex.tcl packages fail if directory de-listed} -setup {
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ }
} -body {
set i [safe::interpCreate -accessPath [list $tcl_library \
[file join $TestsDir auto0 auto1] \
@@ -1251,12 +1464,20 @@ test safe-9.12 {interpConfigure change the access path; pkgIndex.tcl packages fa
list $path1 $path2 $code4 $path4 $code5 $path5 $code3 $msg3 $code6 $msg6 $confA $confB
} -cleanup {
safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:1:)} {\$p(:2:)} 1 {* not found in access path}\
1 {* not found in access path} 1 {*} 1 {*}\
{-accessPath {[list $tcl_library $TestsDir/auto0/auto1 $TestsDir/auto0/auto2]*}\
-statics 1 -nested 0 -deleteHook {}}\
{-accessPath {[list $tcl_library]*} -statics 1 -nested 0 -deleteHook {}}"
test safe-9.12z {interpConfigure change the access path; pkgIndex.tcl packages fail if directory de-listed; zipfs} -setup {
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ }
} -body {
set i [safe::interpCreate -accessPath [list $tcl_library \
[file join $ZipMountPoint auto0 auto1] \
@@ -1285,12 +1506,20 @@ test safe-9.12z {interpConfigure change the access path; pkgIndex.tcl packages f
list $path1 $path2 $code4 $path4 $code5 $path5 $code3 $msg3 $code6 $msg6 $confA $confB
} -cleanup {
safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:1:)} {\$p(:2:)} 1 {* not found in access path}\
1 {* not found in access path} 1 {*} 1 {*}\
{-accessPath {[list $tcl_library $ZipMountPoint/auto0/auto1 $ZipMountPoint/auto0/auto2]*}\
-statics 1 -nested 0 -deleteHook {}}\
{-accessPath {[list $tcl_library]*} -statics 1 -nested 0 -deleteHook {}}"
test safe-9.12opt {interpConfigure change the access path; pkgIndex.tcl packages fail if directory de-listed, uses pkg opt and tcl::idna} -setup {
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ }
} -body {
set i [safe::interpCreate -accessPath [list $tcl_library \
[file join $tcl_library $pkgOptDir] \
@@ -1319,12 +1548,20 @@ test safe-9.12opt {interpConfigure change the access path; pkgIndex.tcl packages
list $path1 $path2 $code4 $path4 $code5 $path5 $code3 $msg3 $code6 $msg6 $confA $confB
} -cleanup {
safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:1:)} {\$p(:2:)} 1 {* not found in access path}\
1 {* not found in access path} 1 {*} 1 {*}\
{-accessPath {[list $tcl_library $tcl_library/$pkgOptDir $tcl_library/$pkgJarDir]*}\
-statics 1 -nested 0 -deleteHook {}}\
{-accessPath {[list $tcl_library]*} -statics 1 -nested 0 -deleteHook {}}"
test safe-9.20 {check module loading} -setup {
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ }
set oldTm [tcl::tm::path list]
foreach path $oldTm {
tcl::tm::path remove $path
@@ -1356,6 +1593,9 @@ test safe-9.20 {check module loading} -setup {
tcl::tm::path add $path
}
safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:1:)} {\$p(:2:)} {\$p(:3:)} -- {{\$p(:1:)}} --\
0 0.5 0 1.0 0 2.0 --\
{-accessPath {[list $tcl_library $TestsDir/auto0/modules \
@@ -1364,6 +1604,11 @@ test safe-9.20 {check module loading} -setup {
-statics 1 -nested 0 -deleteHook {}} --\
res0 res1 res2"
test safe-9.20z {check module loading; zipfs} -setup {
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ }
set oldTm [tcl::tm::path list]
foreach path $oldTm {
tcl::tm::path remove $path
@@ -1395,6 +1640,9 @@ test safe-9.20z {check module loading; zipfs} -setup {
tcl::tm::path add $path
}
safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:1:)} {\$p(:2:)} {\$p(:3:)} -- {{\$p(:1:)}} --\
0 0.5 0 1.0 0 2.0 --\
{-accessPath {[list $tcl_library $ZipMountPoint/auto0/modules \
@@ -1403,6 +1651,11 @@ test safe-9.20z {check module loading; zipfs} -setup {
-statics 1 -nested 0 -deleteHook {}} --\
res0 res1 res2"
test safe-9.21 {interpConfigure change the access path; check module loading; stale data case 1} -setup {
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ }
set oldTm [tcl::tm::path list]
foreach path $oldTm {
tcl::tm::path remove $path
@@ -1453,6 +1706,9 @@ test safe-9.21 {interpConfigure change the access path; check module loading; st
tcl::tm::path add $path
}
safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:1:)} {\$p(:2:)} {\$p(:3:)} -- {{\$p(:1:)}} --\
{\$p(:3:)} {\$p(:4:)} {\$p(:5:)} -- {{\$p(:3:)}} --\
0 0.5 0 1.0 0 2.0 --\
@@ -1470,6 +1726,11 @@ test safe-9.21 {interpConfigure change the access path; check module loading; st
-statics 1 -nested 0 -deleteHook {}} --\
res0 res1 res2"
test safe-9.21z {interpConfigure change the access path; check module loading; stale data case 1; zipfs} -setup {
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ }
set oldTm [tcl::tm::path list]
foreach path $oldTm {
tcl::tm::path remove $path
@@ -1520,6 +1781,9 @@ test safe-9.21z {interpConfigure change the access path; check module loading; s
tcl::tm::path add $path
}
safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:1:)} {\$p(:2:)} {\$p(:3:)} -- {{\$p(:1:)}} --\
{\$p(:3:)} {\$p(:4:)} {\$p(:5:)} -- {{\$p(:3:)}} --\
0 0.5 0 1.0 0 2.0 --\
@@ -1537,6 +1801,11 @@ test safe-9.21z {interpConfigure change the access path; check module loading; s
-statics 1 -nested 0 -deleteHook {}} --\
res0 res1 res2"
test safe-9.22 {interpConfigure change the access path; check module loading; stale data case 0} -setup {
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ }
set oldTm [tcl::tm::path list]
foreach path $oldTm {
tcl::tm::path remove $path
@@ -1582,6 +1851,9 @@ test safe-9.22 {interpConfigure change the access path; check module loading; st
tcl::tm::path add $path
}
safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:1:)} {\$p(:2:)} {\$p(:3:)} -- {{\$p(:1:)}} --\
{\$p(:3:)} {\$p(:4:)} {\$p(:5:)} -- {{\$p(:3:)}} --\
0 0.5 0 1.0 0 2.0 --\
@@ -1599,6 +1871,11 @@ test safe-9.22 {interpConfigure change the access path; check module loading; st
-statics 1 -nested 0 -deleteHook {}} --\
res0 res1 res2"
test safe-9.22z {interpConfigure change the access path; check module loading; stale data case 0; zipfs} -setup {
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ }
set oldTm [tcl::tm::path list]
foreach path $oldTm {
tcl::tm::path remove $path
@@ -1644,6 +1921,9 @@ test safe-9.22z {interpConfigure change the access path; check module loading; s
tcl::tm::path add $path
}
safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:1:)} {\$p(:2:)} {\$p(:3:)} -- {{\$p(:1:)}} --\
{\$p(:3:)} {\$p(:4:)} {\$p(:5:)} -- {{\$p(:3:)}} --\
0 0.5 0 1.0 0 2.0 --\
@@ -1661,6 +1941,11 @@ test safe-9.22z {interpConfigure change the access path; check module loading; s
-statics 1 -nested 0 -deleteHook {}} --\
res0 res1 res2"
test safe-9.23 {interpConfigure change the access path; check module loading; stale data case 3} -setup {
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ }
set oldTm [tcl::tm::path list]
foreach path $oldTm {
tcl::tm::path remove $path
@@ -1717,6 +2002,9 @@ test safe-9.23 {interpConfigure change the access path; check module loading; st
tcl::tm::path add $path
}
safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:1:)} {\$p(:2:)} {\$p(:3:)} -- {{\$p(:1:)}} --\
{\$p(:3:)} {\$p(:4:)} {\$p(:5:)} -- {{\$p(:3:)}} --\
0 0.5 0 1.0 0 2.0 --\
@@ -1734,6 +2022,11 @@ test safe-9.23 {interpConfigure change the access path; check module loading; st
-statics 1 -nested 0 -deleteHook {}} --\
res0 res1 res2"
test safe-9.23z {interpConfigure change the access path; check module loading; stale data case 3; zipfs} -setup {
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ }
set oldTm [tcl::tm::path list]
foreach path $oldTm {
tcl::tm::path remove $path
@@ -1790,6 +2083,9 @@ test safe-9.23z {interpConfigure change the access path; check module loading; s
tcl::tm::path add $path
}
safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:1:)} {\$p(:2:)} {\$p(:3:)} -- {{\$p(:1:)}} --\
{\$p(:3:)} {\$p(:4:)} {\$p(:5:)} -- {{\$p(:3:)}} --\
0 0.5 0 1.0 0 2.0 --\
@@ -1807,6 +2103,11 @@ test safe-9.23z {interpConfigure change the access path; check module loading; s
-statics 1 -nested 0 -deleteHook {}} --\
res0 res1 res2"
test safe-9.24 {interpConfigure change the access path; check module loading; stale data case 2 (worst case)} -setup {
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ }
set oldTm [tcl::tm::path list]
foreach path $oldTm {
tcl::tm::path remove $path
@@ -1857,6 +2158,9 @@ test safe-9.24 {interpConfigure change the access path; check module loading; st
tcl::tm::path add $path
}
safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:1:)} {\$p(:2:)} {\$p(:3:)} -- {{\$p(:1:)}} --\
{\$p(:3:)} {\$p(:4:)} {\$p(:5:)} -- {{\$p(:3:)}} --\
0 0.5 0 1.0 0 2.0 --\
@@ -1874,6 +2178,11 @@ test safe-9.24 {interpConfigure change the access path; check module loading; st
-statics 1 -nested 0 -deleteHook {}} --\
res0 res1 res2"
test safe-9.24z {interpConfigure change the access path; check module loading; stale data case 2 (worst case); zipfs} -setup {
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 1
+ }
set oldTm [tcl::tm::path list]
foreach path $oldTm {
tcl::tm::path remove $path
@@ -1924,6 +2233,9 @@ test safe-9.24z {interpConfigure change the access path; check module loading; s
tcl::tm::path add $path
}
safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
} -match glob -result "{\$p(:1:)} {\$p(:2:)} {\$p(:3:)} -- {{\$p(:1:)}} --\
{\$p(:3:)} {\$p(:4:)} {\$p(:5:)} -- {{\$p(:3:)}} --\
0 0.5 0 1.0 0 2.0 --\
@@ -2363,7 +2675,481 @@ test safe-16.4 {Bug 3529949: defang ~user in globs} -setup {
} -cleanup {
safe::interpDelete $i
} -result {}
-
+
+### 17. The first element in a slave's ::auto_path and access path must be [info library].
+
+test safe-17.1 {Check that first element of slave auto_path (and access path) is Tcl Library} -setup {
+ set lib1 [info library]
+ set lib2 [file dirname $lib1]
+ set ::auto_TMP $::auto_path
+ set ::auto_path [list $lib1 $lib2]
+
+ set i [safe::interpCreate]
+} -body {
+ set autoList {}
+ set token [lindex [$i eval set ::auto_path] 0]
+ set auto0 [dict get [set ::safe::S${i}(access_path,map)] $token]
+ set accessList [lindex [safe::interpConfigure $i -accessPath] 1]
+ return [list [lindex $accessList 0] $auto0]
+} -cleanup {
+ set ::auto_path $::auto_TMP
+ safe::interpDelete $i
+} -result [list [info library] [info library]]
+
+test safe-17.2 {Check that first element of slave auto_path (and access path) is Tcl Library, even if not true for master} -setup {
+ set lib1 [info library]
+ set lib2 [file dirname $lib1]
+ set ::auto_TMP $::auto_path
+ set ::auto_path [list $lib2 $lib1]
+ # Unexpected order, should be reversed in the slave
+
+ set i [safe::interpCreate]
+} -body {
+ set autoList {}
+ set token [lindex [$i eval set ::auto_path] 0]
+ set auto0 [dict get [set ::safe::S${i}(access_path,map)] $token]
+ set accessList [lindex [safe::interpConfigure $i -accessPath] 1]
+
+ return [list [lindex $accessList 0] $auto0]
+} -cleanup {
+ set ::auto_path $::auto_TMP
+ safe::interpDelete $i
+} -result [list [info library] [info library]]
+
+### 18. Tests for AutoSyncDefined without conventional AutoPathSync, i.e. with AutoPathSync off.
+
+test safe-18.1 {cf. safe-7.1 - tests that everything works at high level without conventional AutoPathSync} -constraints AutoSyncDefined -setup {
+ # All ::safe commands are loaded at start of file.
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 0
+ } else {
+ error {This test is meaningful only if the command ::safe::setAutoPathSync is defined}
+ }
+
+ # Without AutoPathSync, we need a more complete auto_path,
+ # because the slave will use the same value.
+ set lib1 [info library]
+ set lib2 [file join $TestsDir auto0]
+ set ::auto_TMP $::auto_path
+ set ::auto_path [list $lib1 $lib2]
+
+ set i [safe::interpCreate]
+ set ::auto_path $::auto_TMP
+} -body {
+ # no error shall occur:
+ # (because the default access_path shall include 1st level sub dirs so
+ # package require in a slave works like in the master)
+ set v [interp eval $i {package require SafeTestPackage1}]
+ # no error shall occur:
+ interp eval $i HeresPackage1
+ set v
+} -cleanup {
+ safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
+} -match glob -result 1.2.3
+test safe-18.1z {cf. safe-7.1 - tests that everything works at high level without conventional AutoPathSync; zipfs} -constraints AutoSyncDefined -setup {
+ # All ::safe commands are loaded at start of file.
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 0
+ } else {
+ error {This test is meaningful only if the command ::safe::setAutoPathSync is defined}
+ }
+
+ # Without AutoPathSync, we need a more complete auto_path,
+ # because the slave will use the same value.
+ set lib1 [info library]
+ set lib2 [file join $ZipMountPoint auto0]
+ set ::auto_TMP $::auto_path
+ set ::auto_path [list $lib1 $lib2]
+
+ set i [safe::interpCreate]
+ set ::auto_path $::auto_TMP
+} -body {
+ # no error shall occur:
+ # (because the default access_path shall include 1st level sub dirs so
+ # package require in a slave works like in the master)
+ set v [interp eval $i {package require SafeTestPackage1}]
+ # no error shall occur:
+ interp eval $i HeresPackage1
+ set v
+} -cleanup {
+ safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
+} -match glob -result 1.2.3
+test safe-18.1opt {cf. safe-7.1opt - tests that everything works at high level without conventional AutoPathSync, use pkg opt} -constraints AutoSyncDefined -setup {
+ # All ::safe commands are loaded at start of file.
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 0
+ } else {
+ error {This test is meaningful only if the command ::safe::setAutoPathSync is defined}
+ }
+
+ # Without AutoPathSync, we need a more complete auto_path,
+ # because the slave will use the same value.
+ set lib1 [info library]
+ set lib2 [file dirname $lib1]
+ set ::auto_TMP $::auto_path
+ set ::auto_path [list $lib1 $lib2]
+
+ set i [safe::interpCreate]
+ set ::auto_path $::auto_TMP
+} -body {
+ # no error shall occur:
+ # (because the default access_path shall include 1st level sub dirs so
+ # package require in a slave works like in the master)
+ set v [interp eval $i {package require opt}]
+ # no error shall occur:
+ interp eval $i {::tcl::Lempty {a list}}
+ set v
+} -cleanup {
+ safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
+} -match glob -result 0.4.*
+test safe-18.2 {cf. safe-7.2 - tests specific path and interpFind/AddToAccessPath without conventional AutoPathSync} -constraints AutoSyncDefined -setup {
+ # All ::safe commands are loaded at start of file.
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 0
+ } else {
+ error {This test is meaningful only if the command ::safe::setAutoPathSync is defined}
+ }
+} -body {
+ set i [safe::interpCreate -nostat -nested 1 -accessPath [list [info library]]]
+ set auto1 [interp eval $i {set ::auto_path}]
+ interp eval $i {set ::auto_path [list {$p(:0:)}]}
+ # should not add anything (p0)
+ set token1 [safe::interpAddToAccessPath $i [info library]]
+ # should add as p* (not p1 if master has a module path)
+ set token2 [safe::interpAddToAccessPath $i "/dummy/unixlike/test/path"]
+ # should add as p* (not p2 if master has a module path)
+ set token3 [safe::interpAddToAccessPath $i [file join $TestsDir auto0]]
+ # an error shall occur (SafeTestPackage1 is not anymore in the secure 0-level
+ # provided deep path)
+ list $auto1 $token1 $token2 $token3 \
+ [catch {interp eval $i {package require SafeTestPackage1}} msg] $msg \
+ [safe::interpConfigure $i]\
+ [safe::interpDelete $i]
+} -cleanup {
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
+} -match glob -result "{} {\$p(:0:)} {\$p(:*:)} {\$p(:*:)}\
+ 1 {can't find package SafeTestPackage1}\
+ {-accessPath {[list $tcl_library \
+ */dummy/unixlike/test/path \
+ $TestsDir/auto0]}\
+ -statics 0 -nested 1 -deleteHook {} -autoPath [list $tcl_library]} {}"
+test safe-18.2z {cf. safe-7.2 - tests specific path and interpFind/AddToAccessPath without conventional AutoPathSync; zipfs} -constraints AutoSyncDefined -setup {
+ # All ::safe commands are loaded at start of file.
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 0
+ } else {
+ error {This test is meaningful only if the command ::safe::setAutoPathSync is defined}
+ }
+} -body {
+ set i [safe::interpCreate -nostat -nested 1 -accessPath [list [info library]]]
+ set auto1 [interp eval $i {set ::auto_path}]
+ interp eval $i {set ::auto_path [list {$p(:0:)}]}
+ # should not add anything (p0)
+ set token1 [safe::interpAddToAccessPath $i [info library]]
+ # should add as p* (not p1 if master has a module path)
+ set token2 [safe::interpAddToAccessPath $i "/dummy/unixlike/test/path"]
+ # should add as p* (not p2 if master has a module path)
+ set token3 [safe::interpAddToAccessPath $i [file join $ZipMountPoint auto0]]
+ # an error shall occur (SafeTestPackage1 is not anymore in the secure 0-level
+ # provided deep path)
+ list $auto1 $token1 $token2 $token3 \
+ [catch {interp eval $i {package require SafeTestPackage1}} msg] $msg \
+ [safe::interpConfigure $i]\
+ [safe::interpDelete $i]
+} -cleanup {
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
+} -match glob -result "{} {\$p(:0:)} {\$p(:*:)} {\$p(:*:)}\
+ 1 {can't find package SafeTestPackage1}\
+ {-accessPath {[list $tcl_library \
+ */dummy/unixlike/test/path \
+ $ZipMountPoint/auto0]}\
+ -statics 0 -nested 1 -deleteHook {} -autoPath [list $tcl_library]} {}"
+test safe-18.2opt {cf. safe-7.2opt - tests specific path and interpFind/AddToAccessPath without conventional AutoPathSync, use pkg opt} -constraints AutoSyncDefined -setup {
+ # All ::safe commands are loaded at start of file.
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 0
+ } else {
+ error {This test is meaningful only if the command ::safe::setAutoPathSync is defined}
+ }
+} -body {
+ set i [safe::interpCreate -nostat -nested 1 -accessPath [list [info library]]]
+ set auto1 [interp eval $i {set ::auto_path}]
+ interp eval $i {set ::auto_path [list {$p(:0:)}]}
+ # should not add anything (p0)
+ set token1 [safe::interpAddToAccessPath $i [info library]]
+ # should add as p* (not p1 if master has a module path)
+ set token2 [safe::interpAddToAccessPath $i "/dummy/unixlike/test/path"]
+ # an error shall occur (opt is not anymore in the secure 0-level
+ # provided deep path)
+ list $auto1 $token1 $token2 \
+ [catch {interp eval $i {package require opt}} msg] $msg \
+ [safe::interpConfigure $i]\
+ [safe::interpDelete $i]
+} -cleanup {
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
+} -match glob -result "{} {\$p(:0:)} {\$p(:*:)} 1 {$pkgOptErrMsg}\
+ {-accessPath {[list $tcl_library */dummy/unixlike/test/path]}\
+ -statics 0 -nested 1 -deleteHook {} -autoPath [list $tcl_library]} {}"
+test safe-18.3 {Check that default auto_path is the same as in the master interpreter without conventional AutoPathSync} -constraints AutoSyncDefined -setup {
+ # All ::safe commands are loaded at start of file.
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 0
+ } else {
+ error {This test is meaningful only if the command ::safe::setAutoPathSync is defined}
+ }
+
+ set i [safe::interpCreate]
+
+} -body {
+ # This file's header sets auto_path to a single directory [info library],
+ # which is the one required by Safe Base to be present & first in the list.
+
+ set ap {}
+ foreach token [$i eval set ::auto_path] {
+ lappend ap [dict get [set ::safe::S${i}(access_path,map)] $token]
+ }
+ return $ap
+} -cleanup {
+ safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
+} -result $::auto_path
+test safe-18.4 {cf. safe-7.4 - tests specific path and positive search and auto_path without conventional AutoPathSync} -constraints AutoSyncDefined -setup {
+ # All ::safe commands are loaded at start of file.
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 0
+ } else {
+ error {This test is meaningful only if the command ::safe::setAutoPathSync is defined}
+ }
+} -body {
+ set i [safe::interpCreate -nostat -nested 1 -accessPath [list [info library]]]
+
+ # should not have been set by Safe Base:
+ set auto1 [interp eval $i {set ::auto_path}]
+
+ interp eval $i {set ::auto_path [list {$p(:0:)}]}
+
+ # should not add anything (p0)
+ set token1 [safe::interpAddToAccessPath $i [info library]]
+
+ # should add as p* (not p1 if master has a module path)
+ set token2 [safe::interpAddToAccessPath $i [file join $TestsDir auto0]]
+
+ # should add as p* (not p2 if master has a module path)
+ set token3 [safe::interpAddToAccessPath $i [file join $TestsDir auto0 auto1]]
+
+ # should not have been changed by Safe Base:
+ set auto2 [interp eval $i {set ::auto_path}]
+
+ set auto3 [interp eval $i [list set ::auto_path [list {$p(:0:)} $token2]]]
+
+ # This time, unlike test safe-18.2 and the try above, SafeTestPackage1 should be found:
+ list $auto1 $auto2 $token1 $token2 $token3 \
+ [catch {interp eval $i {package require SafeTestPackage1}} msg] $msg \
+ [safe::interpConfigure $i]\
+ [safe::interpDelete $i]
+} -cleanup {
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
+} -match glob -result "{} {{\$p(:0:)}} {\$p(:0:)} {\$p(:*:)} {\$p(:*:)} 0 1.2.3\
+ {-accessPath {[list $tcl_library *$TestsDir/auto0 $TestsDir/auto0/auto1]}\
+ -statics 0 -nested 1 -deleteHook {}\
+ -autoPath {[list $tcl_library $TestsDir/auto0]}} {}"
+test safe-18.4z {cf. safe-7.4 - tests specific path and positive search and auto_path without conventional AutoPathSync; zipfs} -constraints AutoSyncDefined -setup {
+ # All ::safe commands are loaded at start of file.
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 0
+ } else {
+ error {This test is meaningful only if the command ::safe::setAutoPathSync is defined}
+ }
+} -body {
+ set i [safe::interpCreate -nostat -nested 1 -accessPath [list [info library]]]
+
+ # should not have been set by Safe Base:
+ set auto1 [interp eval $i {set ::auto_path}]
+
+ interp eval $i {set ::auto_path [list {$p(:0:)}]}
+
+ # should not add anything (p0)
+ set token1 [safe::interpAddToAccessPath $i [info library]]
+
+ # should add as p* (not p1 if master has a module path)
+ set token2 [safe::interpAddToAccessPath $i [file join $ZipMountPoint auto0]]
+
+ # should add as p* (not p2 if master has a module path)
+ set token3 [safe::interpAddToAccessPath $i [file join $ZipMountPoint auto0 auto1]]
+
+ # should not have been changed by Safe Base:
+ set auto2 [interp eval $i {set ::auto_path}]
+
+ set auto3 [interp eval $i [list set ::auto_path [list {$p(:0:)} $token2]]]
+
+ # This time, unlike test safe-18.2 and the try above, SafeTestPackage1 should be found:
+ list $auto1 $auto2 $token1 $token2 $token3 \
+ [catch {interp eval $i {package require SafeTestPackage1}} msg] $msg \
+ [safe::interpConfigure $i]\
+ [safe::interpDelete $i]
+} -cleanup {
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
+} -match glob -result "{} {{\$p(:0:)}} {\$p(:0:)} {\$p(:*:)} {\$p(:*:)} 0 1.2.3\
+ {-accessPath {[list $tcl_library *$ZipMountPoint/auto0 $ZipMountPoint/auto0/auto1]}\
+ -statics 0 -nested 1 -deleteHook {}\
+ -autoPath {[list $tcl_library $ZipMountPoint/auto0]}} {}"
+test safe-18.4opt {cf. safe-7.4opt - tests specific path and positive search and auto_path without conventional AutoPathSync, use pkg opt} -constraints AutoSyncDefined -setup {
+ # All ::safe commands are loaded at start of file.
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 0
+ } else {
+ error {This test is meaningful only if the command ::safe::setAutoPathSync is defined}
+ }
+} -body {
+ set i [safe::interpCreate -nostat -nested 1 -accessPath [list [info library]]]
+
+ # should not have been set by Safe Base:
+ set auto1 [interp eval $i {set ::auto_path}]
+
+ interp eval $i {set ::auto_path [list {$p(:0:)}]}
+
+ # should not add anything (p0)
+ set token1 [safe::interpAddToAccessPath $i [info library]]
+
+ # should add as p* (not p1 if master has a module path)
+ set token2 [safe::interpAddToAccessPath $i [file join [info library] $pkgOptDir]]
+
+ # should not have been changed by Safe Base:
+ set auto2 [interp eval $i {set ::auto_path}]
+
+ # This time, unlike test safe-18.2opt and the try above, opt should be found:
+ list $auto1 $auto2 $token1 $token2 \
+ [catch {interp eval $i {package require opt}} msg] $msg \
+ [safe::interpConfigure $i]\
+ [safe::interpDelete $i]
+} -cleanup {
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
+} -match glob -result "{} {{\$p(:0:)}} {\$p(:0:)} {\$p(:*:)} 0 0.4.*\
+ {-accessPath {[list $tcl_library *$tcl_library/$pkgOptDir]}\
+ -statics 0 -nested 1 -deleteHook {} -autoPath [list $tcl_library]} {}"
+test safe-18.5 {cf. safe-7.5 - tests positive and negative module loading without conventional AutoPathSync} -setup {
+ # All ::safe commands are loaded at start of file.
+ set SyncExists [expr {[info commands ::safe::setAutoPathSync] ne {}}]
+
+ if {$SyncExists} {
+ set SyncVal_TMP [safe::setAutoPathSync]
+ safe::setAutoPathSync 0
+ } else {
+ error {This test is meaningful only if the command ::safe::setAutoPathSync is defined}
+ }
+
+ set i [safe::interpCreate]
+
+ interp eval $i {
+ package forget platform::shell
+ package forget platform
+ catch {namespace delete ::platform}
+ }
+} -body {
+ # Should raise an error (tests module ancestor directory rule)
+ set code1 [catch {interp eval $i {package require shell}} msg1]
+ # Should not raise an error
+ set code2 [catch {interp eval $i {package require platform::shell}} msg2]
+ return [list $code1 $msg1 $code2]
+} -cleanup {
+ safe::interpDelete $i
+ if {$SyncExists} {
+ safe::setAutoPathSync $SyncVal_TMP
+ }
+} -result {1 {can't find package shell} 0}
+
+
+
+### 19. Test tokenization of directories available to a slave.
+
+test safe-19.1 {Check that each directory of the default auto_path is a valid token} -setup {
+ set i [safe::interpCreate]
+} -body {
+ set badTokens {}
+ foreach dir [$i eval {set ::auto_path}] {
+ if {[regexp {^\$p\(:[0-9]+:\)$} $dir]} {
+ # Match - OK - token has expected form
+ } else {
+ # No match - possibly an ordinary path has not been tokenized
+ lappend badTokens $dir
+ }
+ }
+ set badTokens
+} -cleanup {
+ safe::interpDelete $i
+} -result {}
+
+test safe-19.2 {Check that each directory of the module path is a valid token} -setup {
+ set i [safe::interpCreate]
+} -body {
+ set badTokens {}
+ foreach dir [$i eval {::tcl::tm::path list}] {
+ if {[regexp {^\$p\(:[0-9]+:\)$} $dir]} {
+ # Match - OK - token has expected form
+ } else {
+ # No match - possibly an ordinary path has not been tokenized
+ lappend badTokens $dir
+ }
+ }
+ set badTokens
+} -cleanup {
+ safe::interpDelete $i
+} -result {}
+
+
set ::auto_path $saveAutoPath
zipfs unmount ${ZipMountPoint}
unset pkgOptErrMsg pkgOptDir pkgJarDir saveAutoPath TestsDir ZipMountPoint