| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
|
|
| |
| package require opt 0.4.8 |
|
|
| |
| namespace eval ::safe { |
| |
| namespace export interpCreate interpInit interpConfigure interpDelete \ |
| interpAddToAccessPath interpFindInAccessPath setLogCmd |
| } |
|
|
| |
| |
| proc ::safe::InterpStatics {} { |
| foreach v {Args statics noStatics} { |
| upvar $v $v |
| } |
| set flag [::tcl::OptProcArgGiven -noStatics] |
| if {$flag && (!$noStatics == !$statics) |
| && ([::tcl::OptProcArgGiven -statics])} { |
| return -code error\ |
| "conflicting values given for -statics and -noStatics" |
| } |
| if {$flag} { |
| return [expr {!$noStatics}] |
| } else { |
| return $statics |
| } |
| } |
|
|
| |
| |
| proc ::safe::InterpNested {} { |
| foreach v {Args nested nestedLoadOk} { |
| upvar $v $v |
| } |
| set flag [::tcl::OptProcArgGiven -nestedLoadOk] |
| |
| |
| if {$flag && (!$nestedLoadOk != !$nested) |
| && ([::tcl::OptProcArgGiven -nested])} { |
| return -code error\ |
| "conflicting values given for -nested and -nestedLoadOk" |
| } |
| if {$flag} { |
| |
| return $nestedLoadOk |
| } else { |
| return $nested |
| } |
| } |
|
|
| |
| |
| |
| |
| |
|
|
| |
| proc ::safe::interpCreate {args} { |
| set Args [::tcl::OptKeyParse ::safe::interpCreate $args] |
| RejectExcessColons $slave |
| InterpCreate $slave $accessPath \ |
| [InterpStatics] [InterpNested] $deleteHook |
| } |
|
|
| proc ::safe::interpInit {args} { |
| set Args [::tcl::OptKeyParse ::safe::interpIC $args] |
| if {![::interp exists $slave]} { |
| return -code error "\"$slave\" is not an interpreter" |
| } |
| RejectExcessColons $slave |
| InterpInit $slave $accessPath \ |
| [InterpStatics] [InterpNested] $deleteHook |
| } |
|
|
| |
| proc ::safe::CheckInterp {child} { |
| namespace upvar ::safe [VarName $child] state |
| if {![info exists state] || ![::interp exists $child]} { |
| return -code error \ |
| "\"$child\" is not an interpreter managed by ::safe::" |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| proc ::safe::interpConfigure {args} { |
| switch [llength $args] { |
| 1 { |
| |
| |
| |
| |
| set Args [::tcl::OptKeyParse ::safe::interpIC $args] |
| CheckInterp $slave |
| namespace upvar ::safe [VarName $slave] state |
|
|
| return [join [list \ |
| [list -accessPath $state(access_path)] \ |
| [list -statics $state(staticsok)] \ |
| [list -nested $state(nestedok)] \ |
| [list -deleteHook $state(cleanupHook)]]] |
| } |
| 2 { |
| |
| |
| lassign $args slave arg |
|
|
| |
| |
| set desc [lindex [::tcl::OptKeyGetDesc ::safe::interpIC] 2] |
| set hits [::tcl::OptHits desc $arg] |
| if {$hits > 1} { |
| return -code error [::tcl::OptAmbigous $desc $arg] |
| } elseif {$hits == 0} { |
| return -code error [::tcl::OptFlagUsage $desc $arg] |
| } |
| CheckInterp $slave |
| namespace upvar ::safe [VarName $slave] state |
|
|
| set item [::tcl::OptCurDesc $desc] |
| set name [::tcl::OptName $item] |
| switch -exact -- $name { |
| -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 { |
| |
| |
| |
| |
| return -code error\ |
| "ambigous query (get or set -noStatics ?)\ |
| use -statics instead" |
| } |
| -nestedLoadOk { |
| return -code error\ |
| "ambigous query (get or set -nestedLoadOk ?)\ |
| use -nested instead" |
| } |
| default { |
| return -code error "unknown flag $name (bug)" |
| } |
| } |
| } |
| default { |
| |
| |
| set Args [::tcl::OptKeyParse ::safe::interpIC $args] |
| CheckInterp $slave |
| namespace upvar ::safe [VarName $slave] state |
|
|
| |
| |
| if {![::tcl::OptProcArgGiven -accessPath]} { |
| set doreset 0 |
| set accessPath $state(access_path) |
| } else { |
| set doreset 1 |
| } |
| if { |
| ![::tcl::OptProcArgGiven -statics] |
| && ![::tcl::OptProcArgGiven -noStatics] |
| } then { |
| set statics $state(staticsok) |
| } else { |
| set statics [InterpStatics] |
| } |
| if { |
| [::tcl::OptProcArgGiven -nested] || |
| [::tcl::OptProcArgGiven -nestedLoadOk] |
| } then { |
| set nested [InterpNested] |
| } else { |
| set nested $state(nestedok) |
| } |
| if {![::tcl::OptProcArgGiven -deleteHook]} { |
| set deleteHook $state(cleanupHook) |
| } |
| |
| InterpSetConfig $slave $accessPath $statics $nested $deleteHook |
| |
| if {$doreset} { |
| if {[catch {::interp eval $slave {auto_reset}} msg]} { |
| Log $slave "auto_reset failed: $msg" |
| } else { |
| Log $slave "successful auto_reset" NOTICE |
| } |
|
|
| |
| ::interp eval $slave {tcl::tm::path remove {*}[tcl::tm::list]} |
| if {[llength $state(tm_path_slave)] > 0} { |
| ::interp eval $slave [list \ |
| ::tcl::tm::add {*}[lreverse $state(tm_path_slave)]] |
| } |
|
|
| |
| |
| |
| |
| |
| foreach pkg [::interp eval $slave {package names}] { |
| if {[::interp eval $slave [list package provide $pkg]] eq ""} { |
| ::interp eval $slave [list package forget $pkg] |
| } |
| } |
| } |
| return |
| } |
| } |
| } |
|
|
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| proc ::safe::InterpCreate { |
| child |
| access_path |
| staticsok |
| nestedok |
| deletehook |
| } { |
| |
| |
| |
| if {$child ne ""} { |
| namespace eval :: [list ::interp create -safe $child] |
| } else { |
| |
| set child [::interp create -safe] |
| } |
| Log $child "Created" NOTICE |
|
|
| |
| InterpInit $child $access_path $staticsok $nestedok $deletehook |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| proc ::safe::InterpSetConfig {child access_path staticsok nestedok deletehook} { |
| global auto_path |
|
|
| |
| if {$access_path eq ""} { |
| set access_path $auto_path |
|
|
| |
| |
| set where [lsearch -exact $access_path [info library]] |
| if {$where < 0} { |
| |
| set access_path [linsert $access_path 0 [info library]] |
| Log $child "tcl_library was not in auto_path,\ |
| added it to slave's access_path" NOTICE |
| } elseif {$where != 0} { |
| |
| 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 slave's access_path" NOTICE |
| } |
|
|
| |
| |
| |
| set access_path [AddSubDirs $access_path] |
| } |
|
|
| Log $child "Setting accessPath=($access_path) staticsok=$staticsok\ |
| nestedok=$nestedok deletehook=($deletehook)" NOTICE |
|
|
| namespace upvar ::safe [VarName $child] state |
|
|
| |
| |
| |
| |
| |
| |
|
|
| set norm_access_path {} |
| set slave_access_path {} |
| set map_access_path {} |
| set remap_access_path {} |
| set slave_tm_path {} |
|
|
| set i 0 |
| foreach dir $access_path { |
| set token [PathToken $i] |
| 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 morepaths [::tcl::tm::list] |
| set firstpass 1 |
| while {[llength $morepaths]} { |
| set addpaths $morepaths |
| set morepaths {} |
|
|
| foreach dir $addpaths { |
| |
| |
| if {[dict exists $remap_access_path $dir]} { |
| if {$firstpass} { |
| |
| |
| |
| lappend slave_tm_path [dict get $remap_access_path $dir] |
| } |
| continue |
| } |
|
|
| set token [PathToken $i] |
| lappend access_path $dir |
| 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} { |
| |
| |
| |
| lappend slave_tm_path $token |
| } |
| incr i |
|
|
| |
| |
| |
| |
| |
| |
| 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,slave) $slave_access_path |
| set state(tm_path_slave) $slave_tm_path |
| set state(staticsok) $staticsok |
| set state(nestedok) $nestedok |
| set state(cleanupHook) $deletehook |
|
|
| SyncAccessPath $child |
| return |
| } |
|
|
| |
| |
| |
| |
| |
| proc ::safe::interpFindInAccessPath {child path} { |
| CheckInterp $child |
| namespace upvar ::safe [VarName $child] state |
|
|
| if {![dict exists $state(access_path,remap) $path]} { |
| return -code error "$path not found in access path" |
| } |
|
|
| return [dict get $state(access_path,remap) $path] |
| } |
|
|
| |
| |
| |
| |
| proc ::safe::interpAddToAccessPath {child path} { |
| |
| |
| CheckInterp $child |
| namespace upvar ::safe [VarName $child] state |
|
|
| if {[dict exists $state(access_path,remap) $path]} { |
| return [dict get $state(access_path,remap) $path] |
| } |
|
|
| |
| set token [PathToken [llength $state(access_path)]] |
|
|
| lappend state(access_path) $path |
| 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 |
| return $token |
| } |
|
|
| |
| |
| |
| proc ::safe::InterpInit { |
| child |
| access_path |
| staticsok |
| nestedok |
| deletehook |
| } { |
| |
| InterpSetConfig $child $access_path $staticsok $nestedok $deletehook |
|
|
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
|
|
| foreach {command alias} { |
| source AliasSource |
| load AliasLoad |
| encoding AliasEncoding |
| exit interpDelete |
| glob AliasGlob |
| } { |
| ::interp alias $child $command {} [namespace current]::$alias $child |
| } |
|
|
| |
| |
|
|
| ::interp expose $child file |
| foreach subcommand {dirname extension rootname tail} { |
| ::interp alias $child ::tcl::file::$subcommand {} \ |
| ::safe::AliasFileSubcommand $child $subcommand |
| } |
| foreach subcommand { |
| atime attributes copy delete executable exists isdirectory isfile |
| link lstat mtime mkdir nativename normalize owned readable readlink |
| rename size stat tempfile type volumes writable |
| } { |
| ::interp alias $child ::tcl::file::$subcommand {} \ |
| ::safe::BadSubcommand $child file $subcommand |
| } |
|
|
| |
| foreach {subcommand alias} { |
| nameofexecutable AliasExeName |
| } { |
| ::interp alias $child ::tcl::info::$subcommand \ |
| {} [namespace current]::$alias $child |
| } |
|
|
| |
|
|
| |
| |
|
|
| if {[catch {::interp eval $child { |
| 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 slave $child ($msg)" |
| } |
|
|
| if {[catch {::interp eval $child { |
| 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 slave $child ($msg)" |
| } |
|
|
| |
| |
| namespace upvar ::safe [VarName $child] state |
| if {[llength $state(tm_path_slave)] > 0} { |
| ::interp eval $child [list \ |
| ::tcl::tm::add {*}[lreverse $state(tm_path_slave)]] |
| } |
| return $child |
| } |
|
|
| |
| |
| |
| proc ::safe::AddSubDirs {pathList} { |
| set res {} |
| foreach dir $pathList { |
| if {[file isdirectory $dir]} { |
| |
| |
| if {$dir ni $res} { |
| lappend res $dir |
| } |
| foreach sub [glob -directory $dir -nocomplain *] { |
| if {[file isdirectory $sub] && ($sub ni $res)} { |
| |
| lappend res $sub |
| } |
| } |
| } |
| } |
| return $res |
| } |
|
|
| |
| |
| |
| |
| |
|
|
| proc ::safe::interpDelete {child} { |
| Log $child "About to delete" NOTICE |
|
|
| |
| namespace upvar ::safe [VarName $child] state |
|
|
| |
| |
| |
| |
| |
| foreach sub [interp children $child] { |
| if {[info exists ::safe::[VarName [list $child $sub]]]} { |
| ::safe::interpDelete [list $child $sub] |
| } |
| } |
|
|
| |
| |
| |
|
|
| if {[info exists state(cleanupHook)]} { |
| set hook $state(cleanupHook) |
| if {[llength $hook]} { |
| |
| |
| unset state(cleanupHook) |
| try { |
| {*}$hook $child |
| } on error err { |
| Log $child "Delete hook error ($err)" |
| } |
| } |
| } |
|
|
| |
| |
|
|
| if {[info exists state]} { |
| unset state |
| } |
|
|
| |
| |
| if {[::interp exists $child]} { |
| ::interp delete $child |
| Log $child "Deleted" NOTICE |
| } |
|
|
| return |
| } |
|
|
| |
|
|
| proc ::safe::setLogCmd {args} { |
| variable Log |
| set la [llength $args] |
| if {$la == 0} { |
| return $Log |
| } elseif {$la == 1} { |
| set Log [lindex $args 0] |
| } else { |
| set Log $args |
| } |
|
|
| if {$Log eq ""} { |
| |
| |
| proc ::safe::Log {args} {} |
| } else { |
| |
|
|
| proc ::safe::Log {child msg {type ERROR}} { |
| variable Log |
| {*}$Log "$type for slave $child : $msg" |
| return |
| } |
| } |
| } |
|
|
| |
|
|
| |
| |
| |
| |
| proc ::safe::SyncAccessPath {child} { |
| namespace upvar ::safe [VarName $child] state |
|
|
| set slave_access_path $state(access_path,slave) |
| ::interp eval $child [list set auto_path $slave_access_path] |
|
|
| Log $child "auto_path in $child has been set to $slave_access_path"\ |
| NOTICE |
|
|
| |
| |
| |
|
|
| ::interp eval $child [list \ |
| set tcl_library [lindex $slave_access_path 0]] |
| } |
|
|
| |
| proc ::safe::PathToken {n} { |
| |
| |
| return "\$p(:$n:)" |
| } |
|
|
| |
| |
| |
| proc ::safe::TranslatePath {child path} { |
| namespace upvar ::safe [VarName $child] state |
|
|
| |
| |
| if {[string match "*::*" $path] || [string match "*..*" $path]} { |
| return -code error "invalid characters in path $path" |
| } |
|
|
| |
|
|
| return [string map $state(access_path,map) $path] |
| } |
|
|
| |
| |
| proc ::safe::CheckFileName {child file} { |
| |
| |
| |
| |
|
|
| if {![file exists $file]} { |
| |
| return -code error "no such file or directory" |
| } |
|
|
| if {![file readable $file]} { |
| |
| return -code error "not readable" |
| } |
| } |
|
|
| |
| |
| |
|
|
| proc ::safe::AliasFileSubcommand {child subcommand name} { |
| if {[string match ~* $name]} { |
| set name ./$name |
| } |
| tailcall ::interp invokehidden $child tcl:file:$subcommand $name |
| } |
|
|
| |
|
|
| proc ::safe::AliasGlob {child args} { |
| Log $child "GLOB ! $args" NOTICE |
| set cmd {} |
| set at 0 |
| array set got { |
| -directory 0 |
| -nocomplain 0 |
| -join 0 |
| -tails 0 |
| -- 0 |
| } |
|
|
| if {$::tcl_platform(platform) eq "windows"} { |
| set dirPartRE {^(.*)[\\/]([^\\/]*)$} |
| } else { |
| set dirPartRE {^(.*)/([^/]*)$} |
| } |
|
|
| set dir {} |
| set virtualdir {} |
|
|
| while {$at < [llength $args]} { |
| switch -glob -- [set opt [lindex $args $at]] { |
| -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 |
| } |
| -directory { |
| if {$got($opt)} { |
| return -code error \ |
| {"-directory" cannot be used with "-path"} |
| } |
| set got($opt) 1 |
| set virtualdir [lindex $args [incr at]] |
| incr at |
| } |
| -* { |
| Log $child "Safe base rejecting glob option '$opt'" |
| return -code error "Safe base rejecting glob option '$opt'" |
| } |
| default { |
| break |
| } |
| } |
| if {$got(--)} break |
| } |
|
|
| |
| |
| |
| if {$got(-directory)} { |
| try { |
| set dir [TranslatePath $child $virtualdir] |
| DirInAccessPath $child $dir |
| } on error msg { |
| Log $child $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 { |
| |
| |
| |
| Log $child {option -directory must be supplied} |
| if {$got(-nocomplain)} return |
| return -code error "permission denied" |
| } |
|
|
| |
| if {$got(-join)} { |
| set args [lreplace $args $at end [join [lrange $args $at end] "/"]] |
| } |
|
|
| |
| |
|
|
| set firstPattern [llength $cmd] |
| foreach opt [lrange $args $at end] { |
| if {![regexp $dirPartRE $opt -> thedir thefile]} { |
| set thedir . |
| |
| } |
| |
| |
| |
| |
| |
| if {($thedir eq "*") && ($thefile eq "pkgIndex.tcl")} { |
| set mapped 0 |
| foreach d [glob -directory [TranslatePath $child $virtualdir] \ |
| -types d -tails *] { |
| catch { |
| DirInAccessPath $child \ |
| [TranslatePath $child [file join $virtualdir $d]] |
| lappend cmd [file join $d $thefile] |
| set mapped 1 |
| } |
| } |
| if {$mapped} continue |
| |
| |
| |
| |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| try { |
| DirInAccessPath $child [TranslatePath $child \ |
| [file join $virtualdir $thedir]] |
| } on error msg { |
| Log $child $msg |
| if {$got(-nocomplain)} continue |
| return -code error "permission denied" |
| } |
| lappend cmd $opt |
| } |
|
|
| Log $child "GLOB = $cmd" NOTICE |
|
|
| if {$got(-nocomplain) && [llength $cmd] eq $firstPattern} { |
| return |
| } |
| try { |
| |
| |
| |
| |
| |
| |
| |
| set entries [::interp invokehidden $child glob {*}$cmd] |
| } on error msg { |
| |
| |
| Log $child $msg |
| return -code error "script error" |
| } |
|
|
| Log $child "GLOB < $entries" NOTICE |
|
|
| |
| set res {} |
| set l [string length $dir] |
| foreach p $entries { |
| 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 |
| return $res |
| } |
|
|
| |
|
|
| proc ::safe::AliasSource {child args} { |
| set argc [llength $args] |
| |
| |
| if {[lindex $args 0] eq "-encoding"} { |
| incr argc -2 |
| set encoding [lindex $args 1] |
| set at 2 |
| if {$encoding eq "identity"} { |
| Log $child "attempt to use the identity encoding" |
| return -code error "permission denied" |
| } |
| } else { |
| set at 0 |
| set encoding {} |
| } |
| if {$argc != 1} { |
| set msg "wrong # args: should be \"source ?-encoding E? fileName\"" |
| Log $child "$msg ($args)" |
| return -code error $msg |
| } |
| set file [lindex $args $at] |
|
|
| |
| if {[catch { |
| set realfile [TranslatePath $child $file] |
| } msg]} { |
| Log $child $msg |
| return -code error "permission denied" |
| } |
|
|
| |
| if {[catch { |
| FileInAccessPath $child $realfile |
| } msg]} { |
| Log $child $msg |
| return -code error "permission denied" |
| } |
|
|
| |
| |
| |
| |
| if {[catch { |
| CheckFileName $child $realfile |
| } msg]} { |
| Log $child "$realfile:$msg" |
| return -code error -errorcode {POSIX EACCES} $msg |
| } |
|
|
| |
| |
| |
| set old [::interp eval $child {info script}] |
| set replacementMsg "script error" |
| set code [catch { |
| set f [open $realfile] |
| fconfigure $f -eofchar "\x1A {}" |
| if {$encoding ne ""} { |
| fconfigure $f -encoding $encoding |
| } |
| set contents [read $f] |
| close $f |
| ::interp eval $child [list info script $file] |
| } 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]} |
| |
| |
| if {$code == 1} { |
| Log $child $msg |
| return -code error $replacementMsg |
| } |
| return -code $code -options $opt $msg |
| } |
|
|
| |
|
|
| proc ::safe::AliasLoad {child file args} { |
| set argc [llength $args] |
| if {$argc > 2} { |
| set msg "load error: too many arguments" |
| Log $child "$msg ($argc) {$file $args}" |
| return -code error $msg |
| } |
|
|
| |
| set package [lindex $args 0] |
|
|
| namespace upvar ::safe [VarName $child] state |
|
|
| |
| |
| set target [lindex $args 1] |
| if {$target ne ""} { |
| |
| |
| if {!$state(nestedok)} { |
| Log $child "loading to a sub interp (nestedok)\ |
| disabled (trying to load $package to $target)" |
| return -code error "permission denied (nested load)" |
| } |
| } |
|
|
| |
| if {$file eq ""} { |
| |
| if {$package eq ""} { |
| set msg "load error: empty filename and no package name" |
| Log $child $msg |
| return -code error $msg |
| } |
| if {!$state(staticsok)} { |
| Log $child "static packages loading disabled\ |
| (trying to load $package to $target)" |
| return -code error "permission denied (static package)" |
| } |
| } else { |
| |
|
|
| |
| try { |
| set file [TranslatePath $child $file] |
| } on error msg { |
| Log $child $msg |
| return -code error "permission denied" |
| } |
|
|
| |
| try { |
| FileInAccessPath $child $file |
| } on error msg { |
| Log $child $msg |
| return -code error "permission denied (path)" |
| } |
| } |
|
|
| try { |
| return [::interp invokehidden $child load $file $package $target] |
| } on error msg { |
| |
| set msg0 "load of binary library for package $package failed" |
| if {$msg eq {}} { |
| set msg $msg0 |
| } else { |
| set msg "$msg0: $msg" |
| } |
| Log $child $msg |
| return -code error $msg |
| } |
| } |
|
|
| |
| |
|
|
| |
| |
| proc ::safe::FileInAccessPath {child file} { |
| namespace upvar ::safe [VarName $child] state |
| set access_path $state(access_path) |
|
|
| if {[file isdirectory $file]} { |
| return -code error "\"$file\": is a directory" |
| } |
| set parent [file dirname $file] |
|
|
| |
| |
| set norm_parent [file normalize $parent] |
|
|
| namespace upvar ::safe [VarName $child] 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 |
| set access_path $state(access_path) |
|
|
| if {[file isfile $dir]} { |
| return -code error "\"$dir\": is a file" |
| } |
|
|
| |
| |
| set norm_dir [file normalize $dir] |
|
|
| namespace upvar ::safe [VarName $child] state |
| if {$norm_dir ni $state(access_path,norm)} { |
| return -code error "\"$dir\": not in access_path" |
| } |
| } |
|
|
| |
| |
|
|
| 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 |
| } |
|
|
| |
|
|
| proc ::safe::AliasEncoding {child option args} { |
| |
| set subcommands {convertfrom convertto names system} |
| try { |
| set option [tcl::prefix match -error [list -level 1 -errorcode \ |
| [list TCL LOOKUP INDEX option $option]] $subcommands $option] |
| |
| if {$option eq "system" && [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 |
| } |
| tailcall ::interp invokehidden $child encoding $option {*}$args |
| } |
|
|
| |
|
|
| proc ::safe::AliasExeName {child} { |
| return "" |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| 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 |
| } |
| if {[string range $stripped 0 1] eq {::}} { |
| return -code error {interpreter name must not begin "::"} |
| } |
| return |
| } |
|
|
| proc ::safe::VarName {child} { |
| |
| return S[string map {:: @N @ @A} $child] |
| } |
|
|
| proc ::safe::Setup {} { |
| |
| |
| |
| |
| |
|
|
| |
| 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"} |
| }] |
|
|
| |
| ::tcl::OptKeyRegister { |
| {?slave? -name {} "name of the slave (optional)"} |
| } ::safe::interpCreate |
|
|
| |
| |
| lappend ::tcl::OptDesc(::safe::interpCreate) $::tcl::OptDesc($temp) |
|
|
| |
| ::tcl::OptKeyRegister { |
| {slave -name {} "name of the slave"} |
| } ::safe::interpIC |
|
|
| |
| |
| lappend ::tcl::OptDesc(::safe::interpIC) $::tcl::OptDesc($temp) |
|
|
| |
| ::tcl::OptKeyDelete $temp |
|
|
| |
| |
| |
| |
| |
|
|
| setLogCmd {} |
|
|
| |
| |
| |
| return |
| } |
|
|
| namespace eval ::safe { |
| |
|
|
| |
| variable Log {} |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| } |
|
|
| ::safe::Setup |
|
|