⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 safe.tcl

📁 tcl是工具命令语言
💻 TCL
📖 第 1 页 / 共 2 页
字号:
# safe.tcl --## This file provide a safe loading/sourcing mechanism for safe interpreters.# It implements a virtual path mecanism to hide the real pathnames from the# slave. It runs in a master interpreter and sets up data structure and# aliases that will be invoked when used from a slave interpreter.# # See the safe.n man page for details.## Copyright (c) 1996-1997 Sun Microsystems, Inc.## See the file "license.terms" for information on usage and redistribution# of this file, and for a DISCLAIMER OF ALL WARRANTIES.## RCS: @(#) $Id: safe.tcl,v 1.9 2003/02/08 22:03:20 hobbs Exp $## The implementation is based on namespaces. These naming conventions# are followed:# Private procs starts with uppercase.# Public  procs are exported and starts with lowercase## Needed utilities packagepackage require opt 0.4.1;# Create the safe namespacenamespace eval ::safe {    # Exported API:    namespace export interpCreate interpInit interpConfigure interpDelete \	    interpAddToAccessPath interpFindInAccessPath setLogCmd    ####    #    # Setup the arguments parsing    #    ####    # Share the descriptions    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"}    }]    # create case (slave is optional)    ::tcl::OptKeyRegister {	{?slave? -name {} "name of the slave (optional)"}    } ::safe::interpCreate    # adding the flags sub programs to the command program    # (relying on Opt's internal implementation details)    lappend ::tcl::OptDesc(::safe::interpCreate) $::tcl::OptDesc($temp)    # init and configure (slave is needed)    ::tcl::OptKeyRegister {	{slave -name {} "name of the slave"}    } ::safe::interpIC    # adding the flags sub programs to the command program    # (relying on Opt's internal implementation details)    lappend ::tcl::OptDesc(::safe::interpIC) $::tcl::OptDesc($temp)    # temp not needed anymore    ::tcl::OptKeyDelete $temp    # Helper function to resolve the dual way of specifying staticsok    # (either by -noStatics or -statics 0)    proc 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	}    }    # Helper function to resolve the dual way of specifying nested loading    # (either by -nestedLoadOk or -nested 1)    proc InterpNested {} {	foreach v {Args nested nestedLoadOk} {	    upvar $v $v	}	set flag [::tcl::OptProcArgGiven -nestedLoadOk];	# note that the test here is the opposite of the "InterpStatics"	# one (it is not -noNested... because of the wanted default value)	if {$flag && ($nestedLoadOk != $nested) 	          && ([::tcl::OptProcArgGiven -nested])} {	    return -code error\		    "conflicting values given for -nested and -nestedLoadOk"	}	if {$flag} {	    # another difference with "InterpStatics"	    return $nestedLoadOk	} else {	    return $nested	}    }    ####    #    #  API entry points that needs argument parsing :    #    ####    # Interface/entry point function and front end for "Create"    proc interpCreate {args} {	set Args [::tcl::OptKeyParse ::safe::interpCreate $args]	InterpCreate $slave $accessPath \		[InterpStatics] [InterpNested] $deleteHook    }    proc interpInit {args} {	set Args [::tcl::OptKeyParse ::safe::interpIC $args]	if {![::interp exists $slave]} {	    return -code error "\"$slave\" is not an interpreter"	}	InterpInit $slave $accessPath \		[InterpStatics] [InterpNested] $deleteHook;    }    proc CheckInterp {slave} {	if {![IsInterp $slave]} {	    return -code error \		    "\"$slave\" is not an interpreter managed by ::safe::"	}    }    # Interface/entry point function and front end for "Configure"    # This code is awfully pedestrian because it would need    # more coupling and support between the way we store the    # configuration values in safe::interp's and the Opt package    # Obviously we would like an OptConfigure    # to avoid duplicating all this code everywhere. -> TODO    # (the app should share or access easily the program/value    #  stored by opt)    # This is even more complicated by the boolean flags with no values    # that we had the bad idea to support for the sake of user simplicity    # in create/init but which makes life hard in configure...    # So this will be hopefully written and some integrated with opt1.0    # (hopefully for tcl8.1 ?)    proc interpConfigure {args} {	switch [llength $args] {	    1 {		# If we have exactly 1 argument		# the semantic is to return all the current configuration		# We still call OptKeyParse though we know that "slave"		# is our given argument because it also checks		# for the "-help" option.		set Args [::tcl::OptKeyParse ::safe::interpIC $args]		CheckInterp $slave		set res {}		lappend res [list -accessPath [Set [PathListName $slave]]]		lappend res [list -statics    [Set [StaticsOkName $slave]]]		lappend res [list -nested     [Set [NestedOkName $slave]]]		lappend res [list -deleteHook [Set [DeleteHookName $slave]]]		join $res	    }	    2 {		# If we have exactly 2 arguments		# the semantic is a "configure get"		::tcl::Lassign $args slave arg		# get the flag sub program (we 'know' about Opt's internal		# representation of data)		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		set item [::tcl::OptCurDesc $desc]		set name [::tcl::OptName $item]		switch -exact -- $name {		    -accessPath {			return [list -accessPath [Set [PathListName $slave]]]		    }		    -statics {			return [list -statics    [Set [StaticsOkName $slave]]]		    }		    -nested {			return [list -nested     [Set [NestedOkName $slave]]]		    }		    -deleteHook {			return [list -deleteHook [Set [DeleteHookName $slave]]]		    }		    -noStatics {			# it is most probably a set in fact			# but we would need then to jump to the set part			# and it is not *sure* that it is a set action			# that the user want, so force it to use the			# unambigous -statics ?value? instead:			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 {		# Otherwise we want to parse the arguments like init and create		# did		set Args [::tcl::OptKeyParse ::safe::interpIC $args]		CheckInterp $slave		# Get the current (and not the default) values of		# whatever has not been given:		if {![::tcl::OptProcArgGiven -accessPath]} {		    set doreset 1		    set accessPath [Set [PathListName $slave]]		} else {		    set doreset 0		}		if {(![::tcl::OptProcArgGiven -statics]) \			&& (![::tcl::OptProcArgGiven -noStatics]) } {		    set statics    [Set [StaticsOkName $slave]]		} else {		    set statics    [InterpStatics]		}		if {([::tcl::OptProcArgGiven -nested]) \			|| ([::tcl::OptProcArgGiven -nestedLoadOk]) } {		    set nested     [InterpNested]		} else {		    set nested     [Set [NestedOkName $slave]]		}		if {![::tcl::OptProcArgGiven -deleteHook]} {		    set deleteHook [Set [DeleteHookName $slave]]		}		# we can now reconfigure :		InterpSetConfig $slave $accessPath $statics $nested $deleteHook		# auto_reset the slave (to completly synch the new access_path)		if {$doreset} {		    if {[catch {::interp eval $slave {auto_reset}} msg]} {			Log $slave "auto_reset failed: $msg"		    } else {			Log $slave "successful auto_reset" NOTICE		    }		}	    }	}    }    ####    #    #  Functions that actually implements the exported APIs    #    ####    #    # safe::InterpCreate : doing the real job    #    # This procedure creates a safe slave and initializes it with the    # safe base aliases.    # NB: slave name must be simple alphanumeric string, no spaces,    # no (), no {},...  {because the state array is stored as part of the name}    #    # Returns the slave name.    #    # 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.    # + 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)    #                      if 1 : multiple levels are ok.        # use the full name and no indent so auto_mkIndex can find us    proc ::safe::InterpCreate {	slave 	access_path	staticsok	nestedok	deletehook    } {	# Create the slave.	if {$slave ne ""} {	    ::interp create -safe $slave	} else {	    # empty argument: generate slave name	    set slave [::interp create -safe]	}	Log $slave "Created" NOTICE	# Initialize it. (returns slave name)	InterpInit $slave $access_path $staticsok $nestedok $deletehook    }    #    # InterpSetConfig (was setAccessPath) :    #    Sets up slave virtual auto_path and corresponding structure    #    within the master. Also sets the tcl_library in the slave    #    to be the first directory in the path.    #    Nb: If you change the path after the 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.    proc ::safe::InterpSetConfig {slave access_path staticsok\	    nestedok deletehook} {	# determine and store the access path if empty	if {[string equal "" $access_path]} {	    set access_path [uplevel #0 set auto_path]	    # Make sure that tcl_library is in auto_path	    # and at the first position (needed by setAccessPath)	    set where [lsearch -exact $access_path [info library]]	    if {$where == -1} {		# not found, add it.		set access_path [concat [list [info library]] $access_path]		Log $slave "tcl_library was not in auto_path,\			added it to slave's access_path" NOTICE	    } elseif {$where != 0} {		# not first, move it first		set access_path [concat [list [info library]]\			[lreplace $access_path $where $where]]		Log $slave "tcl_libray was not in first in auto_path,\			moved it to front of slave's access_path" NOTICE	    	    }	    # 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]	}	Log $slave "Setting accessPath=($access_path) staticsok=$staticsok\		nestedok=$nestedok deletehook=($deletehook)" NOTICE	# clear old autopath if it existed	set nname [PathNumberName $slave]	if {[Exists $nname]} {	    set n [Set $nname]	    for {set i 0} {$i<$n} {incr i} {		Unset [PathToken $i $slave]	    }	}	# build new one	set slave_auto_path {}	set i 0	foreach dir $access_path {	    Set [PathToken $i $slave] $dir	    lappend slave_auto_path "\$[PathToken $i]"	    incr i	}	Set $nname $i	Set [PathListName $slave] $access_path	Set [VirtualPathListName $slave] $slave_auto_path	Set [StaticsOkName $slave] $staticsok	Set [NestedOkName $slave] $nestedok	Set [DeleteHookName $slave] $deletehook	SyncAccessPath $slave    }    #    #    # FindInAccessPath:    #    Search for a real directory and returns its virtual Id    #    (including the "$")proc ::safe::interpFindInAccessPath {slave path} {	set access_path [GetAccessPath $slave]	set where [lsearch -exact $access_path $path]	if {$where == -1} {	    return -code error "$path not found in access path $access_path"	}	return "\$[PathToken $where]"    }    #    # addToAccessPath:    #    add (if needed) a real directory to access path    #    and return its virtual token (including the "$").proc ::safe::interpAddToAccessPath {slave path} {	# first check if the directory is already in there	if {![catch {interpFindInAccessPath $slave $path} res]} {	    return $res	}	# new one, add it:	set nname [PathNumberName $slave]	set n [Set $nname]	Set [PathToken $n $slave] $path	set token "\$[PathToken $n]"	Lappend [VirtualPathListName $slave] $token	Lappend [PathListName $slave] $path	Set $nname [expr {$n+1}]	SyncAccessPath $slave	return $token    }    # This procedure applies the initializations to an already existing    # interpreter. It is useful when you want to install the safe base    # aliases into a preexisting safe interpreter.    proc ::safe::InterpInit {	slave 	access_path	staticsok	nestedok	deletehook    } {	# Configure will generate an access_path when access_path is	# empty.	InterpSetConfig $slave $access_path $staticsok $nestedok $deletehook	# These aliases let the slave load files to define new commands	# NB we need to add [namespace current], aliases are always	# absolute paths.	::interp alias $slave source {} [namespace current]::AliasSource $slave	::interp alias $slave load {} [namespace current]::AliasLoad $slave	# This alias lets the slave use the encoding names, convertfrom,	# convertto, and system, but not "encoding system <name>" to set	# the system encoding.	::interp alias $slave encoding {} [namespace current]::AliasEncoding \		$slave	# This alias lets the slave have access to a subset of the 'file'	# command functionality.	AliasSubset $slave file file dir.* join root.* ext.* tail \		path.* split	# This alias interposes on the 'exit' command and cleanly terminates	# the slave.	::interp alias $slave exit {} [namespace current]::interpDelete $slave

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -