summaryrefslogtreecommitdiffstats
path: root/contrib/tcl/library
diff options
context:
space:
mode:
authorphk <phk@FreeBSD.org>1997-10-01 13:19:13 +0000
committerphk <phk@FreeBSD.org>1997-10-01 13:19:13 +0000
commit5b30c2fb530aac2933dce3197e33362c844d3039 (patch)
treebca582e352640f318b35228d0c250ddde3bd0e0b /contrib/tcl/library
parent30db38624722a51670556ef9b2dd7ccf4fb57387 (diff)
downloadFreeBSD-src-5b30c2fb530aac2933dce3197e33362c844d3039.zip
FreeBSD-src-5b30c2fb530aac2933dce3197e33362c844d3039.tar.gz
Upgrade to 8.0 release.
Diffstat (limited to 'contrib/tcl/library')
-rw-r--r--contrib/tcl/library/history.tcl369
-rw-r--r--contrib/tcl/library/http1.0/http.tcl10
-rw-r--r--contrib/tcl/library/http2.0/http.tcl460
-rw-r--r--contrib/tcl/library/http2.0/pkgIndex.tcl11
-rw-r--r--contrib/tcl/library/init.tcl106
-rw-r--r--contrib/tcl/library/opt0.1/optparse.tcl1067
-rw-r--r--contrib/tcl/library/opt0.1/pkgIndex.tcl7
-rw-r--r--contrib/tcl/library/safe.tcl710
-rw-r--r--contrib/tcl/library/tclIndex37
9 files changed, 2721 insertions, 56 deletions
diff --git a/contrib/tcl/library/history.tcl b/contrib/tcl/library/history.tcl
new file mode 100644
index 0000000..a6beb43
--- /dev/null
+++ b/contrib/tcl/library/history.tcl
@@ -0,0 +1,369 @@
+# history.tcl --
+#
+# Implementation of the history command.
+#
+# SCCS: @(#) history.tcl 1.7 97/08/07 16:45:50
+#
+# Copyright (c) 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.
+#
+
+# The tcl::history array holds the history list and
+# some additional bookkeeping variables.
+#
+# nextid the index used for the next history list item.
+# keep the max size of the history list
+# oldest the index of the oldest item in the history.
+
+namespace eval tcl {
+ variable history
+ if ![info exists history] {
+ array set history {
+ nextid 0
+ keep 20
+ oldest -20
+ }
+ }
+}
+
+# history --
+#
+# This is the main history command. See the man page for its interface.
+# This does argument checking and calls helper procedures in the
+# history namespace.
+
+proc history {args} {
+ set len [llength $args]
+ if {$len == 0} {
+ return [tcl::HistInfo]
+ }
+ set key [lindex $args 0]
+ set options "add, change, clear, event, info, keep, nextid, or redo"
+ switch -glob -- $key {
+ a* { # history add
+
+ if {$len > 3} {
+ return -code error "wrong # args: should be \"history add event ?exec?\""
+ }
+ if {![string match $key* add]} {
+ return -code error "bad option \"$key\": must be $options"
+ }
+ if {$len == 3} {
+ set arg [lindex $args 2]
+ if {! ([string match e* $arg] && [string match $arg* exec])} {
+ return -code error "bad argument \"$arg\": should be \"exec\""
+ }
+ }
+ return [tcl::HistAdd [lindex $args 1] [lindex $args 2]]
+ }
+ ch* { # history change
+
+ if {($len > 3) || ($len < 2)} {
+ return -code error "wrong # args: should be \"history change newValue ?event?\""
+ }
+ if {![string match $key* change]} {
+ return -code error "bad option \"$key\": must be $options"
+ }
+ if {$len == 2} {
+ set event 0
+ } else {
+ set event [lindex $args 2]
+ }
+
+ return [tcl::HistChange [lindex $args 1] $event]
+ }
+ cl* { # history clear
+
+ if {($len > 1)} {
+ return -code error "wrong # args: should be \"history clear\""
+ }
+ if {![string match $key* clear]} {
+ return -code error "bad option \"$key\": must be $options"
+ }
+ return [tcl::HistClear]
+ }
+ e* { # history event
+
+ if {$len > 2} {
+ return -code error "wrong # args: should be \"history event ?event?\""
+ }
+ if {![string match $key* event]} {
+ return -code error "bad option \"$key\": must be $options"
+ }
+ if {$len == 1} {
+ set event -1
+ } else {
+ set event [lindex $args 1]
+ }
+ return [tcl::HistEvent $event]
+ }
+ i* { # history info
+
+ if {$len > 2} {
+ return -code error "wrong # args: should be \"history info ?count?\""
+ }
+ if {![string match $key* info]} {
+ return -code error "bad option \"$key\": must be $options"
+ }
+ return [tcl::HistInfo [lindex $args 1]]
+ }
+ k* { # history keep
+
+ if {$len > 2} {
+ return -code error "wrong # args: should be \"history keep ?count?\""
+ }
+ if {$len == 1} {
+ return [tcl::HistKeep]
+ } else {
+ set limit [lindex $args 1]
+ if {[catch {expr $limit}] || ($limit < 0)} {
+ return -code error "illegal keep count \"$limit\""
+ }
+ return [tcl::HistKeep $limit]
+ }
+ }
+ n* { # history nextid
+
+ if {$len > 1} {
+ return -code error "wrong # args: should be \"history nextid\""
+ }
+ if {![string match $key* nextid]} {
+ return -code error "bad option \"$key\": must be $options"
+ }
+ return [expr $tcl::history(nextid) + 1]
+ }
+ r* { # history redo
+
+ if {$len > 2} {
+ return -code error "wrong # args: should be \"history redo ?event?\""
+ }
+ if {![string match $key* redo]} {
+ return -code error "bad option \"$key\": must be $options"
+ }
+ return [tcl::HistRedo [lindex $args 1]]
+ }
+ default {
+ return -code error "bad option \"$key\": must be $options"
+ }
+ }
+}
+
+# tcl::HistAdd --
+#
+# Add an item to the history, and optionally eval it at the global scope
+#
+# Parameters:
+# command the command to add
+# exec (optional) a substring of "exec" causes the
+# command to be evaled.
+# Results:
+# If executing, then the results of the command are returned
+#
+# Side Effects:
+# Adds to the history list
+
+ proc tcl::HistAdd {command {exec {}}} {
+ variable history
+ set i [incr history(nextid)]
+ set history($i) $command
+ set j [incr history(oldest)]
+ if {[info exists history($j)]} {unset history($j)}
+ if {[string match e* $exec]} {
+ return [uplevel #0 $command]
+ } else {
+ return {}
+ }
+}
+
+# tcl::HistKeep --
+#
+# Set or query the limit on the length of the history list
+#
+# Parameters:
+# limit (optional) the length of the history list
+#
+# Results:
+# If no limit is specified, the current limit is returned
+#
+# Side Effects:
+# Updates history(keep) if a limit is specified
+
+ proc tcl::HistKeep {{limit {}}} {
+ variable history
+ if {[string length $limit] == 0} {
+ return $history(keep)
+ } else {
+ set oldold $history(oldest)
+ set history(oldest) [expr $history(nextid) - $limit]
+ for {} {$oldold <= $history(oldest)} {incr oldold} {
+ if {[info exists history($oldold)]} {unset history($oldold)}
+ }
+ set history(keep) $limit
+ }
+}
+
+# tcl::HistClear --
+#
+# Erase the history list
+#
+# Parameters:
+# none
+#
+# Results:
+# none
+#
+# Side Effects:
+# Resets the history array, except for the keep limit
+
+ proc tcl::HistClear {} {
+ variable history
+ set keep $history(keep)
+ unset history
+ array set history [list \
+ nextid 0 \
+ keep $keep \
+ oldest -$keep \
+ ]
+}
+
+# tcl::HistInfo --
+#
+# Return a pretty-printed version of the history list
+#
+# Parameters:
+# num (optional) the length of the history list to return
+#
+# Results:
+# A formatted history list
+
+ proc tcl::HistInfo {{num {}}} {
+ variable history
+ if {$num == {}} {
+ set num [expr $history(keep) + 1]
+ }
+ set result {}
+ set newline ""
+ for {set i [expr $history(nextid) - $num + 1]} \
+ {$i <= $history(nextid)} {incr i} {
+ if ![info exists history($i)] {
+ continue
+ }
+ set cmd [string trimright $history($i) \ \n]
+ regsub -all \n $cmd "\n\t" cmd
+ append result $newline[format "%6d %s" $i $cmd]
+ set newline \n
+ }
+ return $result
+}
+
+# tcl::HistRedo --
+#
+# Fetch the previous or specified event, execute it, and then
+# replace the current history item with that event.
+#
+# Parameters:
+# event (optional) index of history item to redo. Defaults to -1,
+# which means the previous event.
+#
+# Results:
+# Those of the command being redone.
+#
+# Side Effects:
+# Replaces the current history list item with the one being redone.
+
+ proc tcl::HistRedo {{event -1}} {
+ variable history
+ if {[string length $event] == 0} {
+ set event -1
+ }
+ set i [HistIndex $event]
+ if {$i == $history(nextid)} {
+ return -code error "cannot redo the current event"
+ }
+ set cmd $history($i)
+ HistChange $cmd 0
+ uplevel #0 $cmd
+}
+
+# tcl::HistIndex --
+#
+# Map from an event specifier to an index in the history list.
+#
+# Parameters:
+# event index of history item to redo.
+# If this is a positive number, it is used directly.
+# If it is a negative number, then it counts back to a previous
+# event, where -1 is the most recent event.
+# A string can be matched, either by being the prefix of
+# a command or by matching a command with string match.
+#
+# Results:
+# The index into history, or an error if the index didn't match.
+
+ proc tcl::HistIndex {event} {
+ variable history
+ if {[catch {expr $event}]} {
+ for {set i $history(nextid)} {[info exists history($i)]} {incr i -1} {
+ if {[string match $event* $history($i)]} {
+ return $i;
+ }
+ if {[string match $event $history($i)]} {
+ return $i;
+ }
+ }
+ return -code error "no event matches \"$event\""
+ } elseif {$event <= 0} {
+ set i [expr $history(nextid) + $event]
+ } else {
+ set i $event
+ }
+ if {$i <= $history(oldest)} {
+ return -code error "event \"$event\" is too far in the past"
+ }
+ if {$i > $history(nextid)} {
+ return -code error "event \"$event\" hasn't occured yet"
+ }
+ return $i
+}
+
+# tcl::HistEvent --
+#
+# Map from an event specifier to the value in the history list.
+#
+# Parameters:
+# event index of history item to redo. See index for a
+# description of possible event patterns.
+#
+# Results:
+# The value from the history list.
+
+ proc tcl::HistEvent {event} {
+ variable history
+ set i [HistIndex $event]
+ if {[info exists history($i)]} {
+ return [string trimright $history($i) \ \n]
+ } else {
+ return "";
+ }
+}
+
+# tcl::HistChange --
+#
+# Replace a value in the history list.
+#
+# Parameters:
+# cmd The new value to put into the history list.
+# event (optional) index of history item to redo. See index for a
+# description of possible event patterns. This defaults
+# to 0, which specifies the current event.
+#
+# Side Effects:
+# Changes the history list.
+
+ proc tcl::HistChange {cmd {event 0}} {
+ variable history
+ set i [HistIndex $event]
+ set history($i) $cmd
+}
diff --git a/contrib/tcl/library/http1.0/http.tcl b/contrib/tcl/library/http1.0/http.tcl
index 366b3ed..450d643 100644
--- a/contrib/tcl/library/http1.0/http.tcl
+++ b/contrib/tcl/library/http1.0/http.tcl
@@ -5,7 +5,7 @@
# These procedures use a callback interface to avoid using vwait,
# which is not defined in the safe base.
#
-# SCCS: @(#) http.tcl 1.6 97/05/20 18:09:27
+# SCCS: @(#) http.tcl 1.8 97/07/22 13:37:20
#
# See the http.n man page for documentation
@@ -118,13 +118,16 @@ proc http_get { url args } {
return -code error "Unknown option $flag, can be: $usage"
}
}
- if {! [regexp -nocase {^(http://)?([^/:]+)(:([0-9]+))?(/.*)} $url \
+ if {! [regexp -nocase {^(http://)?([^/:]+)(:([0-9]+))?(/.*)?$} $url \
x proto host y port srvurl]} {
error "Unsupported URL: $url"
}
if {[string length $port] == 0} {
set port 80
}
+ if {[string length $srvurl] == 0} {
+ set srvurl /
+ }
if {[string length $proto] == 0} {
set url http://$url
}
@@ -221,6 +224,9 @@ proc http_size {token} {
if ![regexp -nocase ^text $state(type)] {
# Turn off conversions for non-text data
fconfigure $s -translation binary
+ if {[info exists state(-channel)]} {
+ fconfigure $state(-channel) -translation binary
+ }
}
if {[info exists state(-channel)] &&
![info exists state(-handler)]} {
diff --git a/contrib/tcl/library/http2.0/http.tcl b/contrib/tcl/library/http2.0/http.tcl
new file mode 100644
index 0000000..80fbfc6
--- /dev/null
+++ b/contrib/tcl/library/http2.0/http.tcl
@@ -0,0 +1,460 @@
+# http.tcl --
+#
+# Client-side HTTP for GET, POST, and HEAD commands.
+# These routines can be used in untrusted code that uses
+# the Safesock security policy. These procedures use a
+# callback interface to avoid using vwait, which is not
+# defined in the safe base.
+#
+# See the file "license.terms" for information on usage and
+# redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+# SCCS: @(#) http.tcl 1.6 97/08/07 16:48:32
+
+package provide http 2.0 ;# This uses Tcl namespaces
+
+namespace eval http {
+ variable http
+
+ array set http {
+ -accept */*
+ -proxyhost {}
+ -proxyport {}
+ -useragent {Tcl http client package 2.0}
+ -proxyfilter http::ProxyRequired
+ }
+
+ variable formMap
+ set alphanumeric a-zA-Z0-9
+
+ for {set i 1} {$i <= 256} {incr i} {
+ set c [format %c $i]
+ if {![string match \[$alphanumeric\] $c]} {
+ set formMap($c) %[format %.2x $i]
+ }
+ }
+ # These are handled specially
+ array set formMap {
+ " " + \n %0d%0a
+ }
+
+ namespace export geturl config reset wait formatQuery
+ # Useful, but not exported: data size status code
+}
+
+# http::config --
+#
+# See documentaion for details.
+#
+# Arguments:
+# args Options parsed by the procedure.
+# Results:
+# TODO
+
+proc http::config {args} {
+ variable http
+ set options [lsort [array names http -*]]
+ set usage [join $options ", "]
+ if {[llength $args] == 0} {
+ set result {}
+ foreach name $options {
+ lappend result $name $http($name)
+ }
+ return $result
+ }
+ regsub -all -- - $options {} options
+ set pat ^-([join $options |])$
+ if {[llength $args] == 1} {
+ set flag [lindex $args 0]
+ if {[regexp -- $pat $flag]} {
+ return $http($flag)
+ } else {
+ return -code error "Unknown option $flag, must be: $usage"
+ }
+ } else {
+ foreach {flag value} $args {
+ if [regexp -- $pat $flag] {
+ set http($flag) $value
+ } else {
+ return -code error "Unknown option $flag, must be: $usage"
+ }
+ }
+ }
+}
+
+ proc http::Finish { token {errormsg ""} } {
+ variable $token
+ upvar 0 $token state
+ global errorInfo errorCode
+ if {[string length $errormsg] != 0} {
+ set state(error) [list $errormsg $errorInfo $errorCode]
+ set state(status) error
+ }
+ catch {close $state(sock)}
+ catch {after cancel $state(after)}
+ if {[info exists state(-command)]} {
+ if {[catch {eval $state(-command) {$token}} err]} {
+ if {[string length $errormsg] == 0} {
+ set state(error) [list $err $errorInfo $errorCode]
+ set state(status) error
+ }
+ }
+ unset state(-command)
+ }
+}
+
+# http::reset --
+#
+# See documentaion for details.
+#
+# Arguments:
+# token Connection token.
+# why Status info.
+# Results:
+# TODO
+
+proc http::reset { token {why reset} } {
+ variable $token
+ upvar 0 $token state
+ set state(status) $why
+ catch {fileevent $state(sock) readable {}}
+ Finish $token
+ if {[info exists state(error)]} {
+ set errorlist $state(error)
+ unset state(error)
+ eval error $errorlist
+ }
+}
+
+# http::geturl --
+#
+# Establishes a connection to a remote url via http.
+#
+# Arguments:
+# url The http URL to goget.
+# args Option value pairs. Valid options include:
+# -blocksize, -validate, -headers, -timeout
+# Results:
+# Returns a token for this connection.
+
+
+proc http::geturl { url args } {
+ variable http
+ if ![info exists http(uid)] {
+ set http(uid) 0
+ }
+ set token [namespace current]::[incr http(uid)]
+ variable $token
+ upvar 0 $token state
+ reset $token
+ array set state {
+ -blocksize 8192
+ -validate 0
+ -headers {}
+ -timeout 0
+ state header
+ meta {}
+ currentsize 0
+ totalsize 0
+ type text/html
+ body {}
+ status ""
+ }
+ set options {-blocksize -channel -command -handler -headers \
+ -progress -query -validate -timeout}
+ set usage [join $options ", "]
+ regsub -all -- - $options {} options
+ set pat ^-([join $options |])$
+ foreach {flag value} $args {
+ if [regexp $pat $flag] {
+ # Validate numbers
+ if {[info exists state($flag)] && \
+ [regexp {^[0-9]+$} $state($flag)] && \
+ ![regexp {^[0-9]+$} $value]} {
+ return -code error "Bad value for $flag ($value), must be integer"
+ }
+ set state($flag) $value
+ } else {
+ return -code error "Unknown option $flag, can be: $usage"
+ }
+ }
+ if {! [regexp -nocase {^(http://)?([^/:]+)(:([0-9]+))?(/.*)?$} $url \
+ x proto host y port srvurl]} {
+ error "Unsupported URL: $url"
+ }
+ if {[string length $port] == 0} {
+ set port 80
+ }
+ if {[string length $srvurl] == 0} {
+ set srvurl /
+ }
+ if {[string length $proto] == 0} {
+ set url http://$url
+ }
+ set state(url) $url
+ if {![catch {$http(-proxyfilter) $host} proxy]} {
+ set phost [lindex $proxy 0]
+ set pport [lindex $proxy 1]
+ }
+ if {$state(-timeout) > 0} {
+ set state(after) [after $state(-timeout) [list http::reset $token timeout]]
+ }
+ if {[info exists phost] && [string length $phost]} {
+ set srvurl $url
+ set s [socket $phost $pport]
+ } else {
+ set s [socket $host $port]
+ }
+ set state(sock) $s
+
+ # Send data in cr-lf format, but accept any line terminators
+
+ fconfigure $s -translation {auto crlf} -buffersize $state(-blocksize)
+
+ # The following is disallowed in safe interpreters, but the socket
+ # is already in non-blocking mode in that case.
+
+ catch {fconfigure $s -blocking off}
+ set len 0
+ set how GET
+ if {[info exists state(-query)]} {
+ set len [string length $state(-query)]
+ if {$len > 0} {
+ set how POST
+ }
+ } elseif {$state(-validate)} {
+ set how HEAD
+ }
+ puts $s "$how $srvurl HTTP/1.0"
+ puts $s "Accept: $http(-accept)"
+ puts $s "Host: $host"
+ puts $s "User-Agent: $http(-useragent)"
+ foreach {key value} $state(-headers) {
+ regsub -all \[\n\r\] $value {} value
+ set key [string trim $key]
+ if {[string length $key]} {
+ puts $s "$key: $value"
+ }
+ }
+ if {$len > 0} {
+ puts $s "Content-Length: $len"
+ puts $s "Content-Type: application/x-www-form-urlencoded"
+ puts $s ""
+ fconfigure $s -translation {auto binary}
+ puts $s $state(-query)
+ } else {
+ puts $s ""
+ }
+ flush $s
+ fileevent $s readable [list http::Event $token]
+ if {! [info exists state(-command)]} {
+ wait $token
+ }
+ return $token
+}
+
+# Data access functions:
+# Data - the URL data
+# Status - the transaction status: ok, reset, eof, timeout
+# Code - the HTTP transaction code, e.g., 200
+# Size - the size of the URL data
+
+proc http::data {token} {
+ variable $token
+ upvar 0 $token state
+ return $state(body)
+}
+proc http::status {token} {
+ variable $token
+ upvar 0 $token state
+ return $state(status)
+}
+proc http::code {token} {
+ variable $token
+ upvar 0 $token state
+ return $state(http)
+}
+proc http::size {token} {
+ variable $token
+ upvar 0 $token state
+ return $state(currentsize)
+}
+
+ proc http::Event {token} {
+ variable $token
+ upvar 0 $token state
+ set s $state(sock)
+
+ if [::eof $s] then {
+ Eof $token
+ return
+ }
+ if {$state(state) == "header"} {
+ set n [gets $s line]
+ if {$n == 0} {
+ set state(state) body
+ if ![regexp -nocase ^text $state(type)] {
+ # Turn off conversions for non-text data
+ fconfigure $s -translation binary
+ if {[info exists state(-channel)]} {
+ fconfigure $state(-channel) -translation binary
+ }
+ }
+ if {[info exists state(-channel)] &&
+ ![info exists state(-handler)]} {
+ # Initiate a sequence of background fcopies
+ fileevent $s readable {}
+ CopyStart $s $token
+ }
+ } elseif {$n > 0} {
+ if [regexp -nocase {^content-type:(.+)$} $line x type] {
+ set state(type) [string trim $type]
+ }
+ if [regexp -nocase {^content-length:(.+)$} $line x length] {
+ set state(totalsize) [string trim $length]
+ }
+ if [regexp -nocase {^([^:]+):(.+)$} $line x key value] {
+ lappend state(meta) $key $value
+ } elseif {[regexp ^HTTP $line]} {
+ set state(http) $line
+ }
+ }
+ } else {
+ if [catch {
+ if {[info exists state(-handler)]} {
+ set n [eval $state(-handler) {$s $token}]
+ } else {
+ set block [read $s $state(-blocksize)]
+ set n [string length $block]
+ if {$n >= 0} {
+ append state(body) $block
+ }
+ }
+ if {$n >= 0} {
+ incr state(currentsize) $n
+ }
+ } err] {
+ Finish $token $err
+ } else {
+ if [info exists state(-progress)] {
+ eval $state(-progress) {$token $state(totalsize) $state(currentsize)}
+ }
+ }
+ }
+}
+ proc http::CopyStart {s token} {
+ variable $token
+ upvar 0 $token state
+ if [catch {
+ fcopy $s $state(-channel) -size $state(-blocksize) -command \
+ [list http::CopyDone $token]
+ } err] {
+ Finish $token $err
+ }
+}
+ proc http::CopyDone {token count} {
+ variable $token
+ upvar 0 $token state
+ set s $state(sock)
+ incr state(currentsize) $count
+ if [info exists state(-progress)] {
+ eval $state(-progress) {$token $state(totalsize) $state(currentsize)}
+ }
+ if [::eof $s] {
+ Eof $token
+ } else {
+ CopyStart $s $token
+ }
+}
+ proc http::Eof {token} {
+ variable $token
+ upvar 0 $token state
+ if {$state(state) == "header"} {
+ # Premature eof
+ set state(status) eof
+ } else {
+ set state(status) ok
+ }
+ set state(state) eof
+ Finish $token
+}
+
+# http::wait --
+#
+# See documentaion for details.
+#
+# Arguments:
+# token Connection token.
+# Results:
+# The status after the wait.
+
+proc http::wait {token} {
+ variable $token
+ upvar 0 $token state
+
+ if {![info exists state(status)] || [string length $state(status)] == 0} {
+ vwait $token\(status)
+ }
+ if {[info exists state(error)]} {
+ set errorlist $state(error)
+ unset state(error)
+ eval error $errorlist
+ }
+ return $state(status)
+}
+
+# http::formatQuery --
+#
+# See documentaion for details.
+# Call http::formatQuery with an even number of arguments, where
+# the first is a name, the second is a value, the third is another
+# name, and so on.
+#
+# Arguments:
+# args A list of name-value pairs.
+# Results:
+# TODO
+
+proc http::formatQuery {args} {
+ set result ""
+ set sep ""
+ foreach i $args {
+ append result $sep [mapReply $i]
+ if {$sep != "="} {
+ set sep =
+ } else {
+ set sep &
+ }
+ }
+ return $result
+}
+
+# do x-www-urlencoded character mapping
+# The spec says: "non-alphanumeric characters are replaced by '%HH'"
+# 1 leave alphanumerics characters alone
+# 2 Convert every other character to an array lookup
+# 3 Escape constructs that are "special" to the tcl parser
+# 4 "subst" the result, doing all the array substitutions
+
+ proc http::mapReply {string} {
+ variable formMap
+ set alphanumeric a-zA-Z0-9
+ regsub -all \[^$alphanumeric\] $string {$formMap(&)} string
+ regsub -all \n $string {\\n} string
+ regsub -all \t $string {\\t} string
+ regsub -all {[][{})\\]\)} $string {\\&} string
+ return [subst $string]
+}
+
+# Default proxy filter.
+ proc http::ProxyRequired {host} {
+ variable http
+ if {[info exists http(-proxyhost)] && [string length $http(-proxyhost)]} {
+ if {![info exists http(-proxyport)] || ![string length $http(-proxyport)]} {
+ set http(-proxyport) 8080
+ }
+ return [list $http(-proxyhost) $http(-proxyport)]
+ } else {
+ return {}
+ }
+}
diff --git a/contrib/tcl/library/http2.0/pkgIndex.tcl b/contrib/tcl/library/http2.0/pkgIndex.tcl
new file mode 100644
index 0000000..01052f3
--- /dev/null
+++ b/contrib/tcl/library/http2.0/pkgIndex.tcl
@@ -0,0 +1,11 @@
+# Tcl package index file, version 1.0
+# This file is generated by the "pkg_mkIndex" command
+# and sourced either when an application starts up or
+# by a "package unknown" script. It invokes the
+# "package ifneeded" command to set up package-related
+# information so that packages will be loaded automatically
+# in response to "package require" commands. When this
+# script is sourced, the variable $dir must contain the
+# full path name of this file's directory.
+
+package ifneeded http 2.0 [list tclPkgSetup $dir http 2.0 {{http.tcl source {::http::config ::http::formatQuery ::http::geturl ::http::reset ::http::wait}}}]
diff --git a/contrib/tcl/library/init.tcl b/contrib/tcl/library/init.tcl
index 43bd37c..1985224 100644
--- a/contrib/tcl/library/init.tcl
+++ b/contrib/tcl/library/init.tcl
@@ -3,7 +3,7 @@
# Default system startup file for Tcl-based applications. Defines
# "unknown" procedure and auto-load facilities.
#
-# SCCS: @(#) init.tcl 1.79 97/06/24 17:18:54
+# SCCS: @(#) init.tcl 1.86 97/08/08 10:37:39
#
# Copyright (c) 1991-1993 The Regents of the University of California.
# Copyright (c) 1994-1996 Sun Microsystems, Inc.
@@ -18,9 +18,11 @@ if {[info commands package] == ""} {
package require -exact Tcl 8.0
# Compute the auto path to use in this interpreter.
-
-if [catch {set auto_path $env(TCLLIBPATH)}] {
- set auto_path ""
+# (auto_path could be already set, in safe interps for instance)
+if {![info exists auto_path]} {
+ if [catch {set auto_path $env(TCLLIBPATH)}] {
+ set auto_path ""
+ }
}
if {[lsearch -exact $auto_path [info library]] < 0} {
lappend auto_path [info library]
@@ -47,6 +49,14 @@ if {[info commands exec] == ""} {
set errorCode ""
set errorInfo ""
+# Define a log command (which can be overwitten to log errors
+# differently, specially when stderr is not available)
+
+if {[info commands tclLog] == ""} {
+ proc tclLog {string} {
+ catch {puts stderr $string}
+ }
+}
# unknown --
# This procedure is called when a Tcl command is invoked that doesn't
@@ -132,14 +142,17 @@ proc unknown args {
set errorCode $savedErrorCode
set errorInfo $savedErrorInfo
if {$name == "!!"} {
-# return [uplevel {history redo}]
- return -code error "!! is disabled until history is fixed in Tcl8.0"
+ set newcmd [history event]
+ } elseif {[regexp {^!(.+)$} $name dummy event]} {
+ set newcmd [history event $event]
+ } elseif {[regexp {^\^([^^]*)\^([^^]*)\^?$} $name dummy old new]} {
+ set newcmd [history event -1]
+ catch {regsub -all -- $old $newcmd $new newcmd}
}
- if [regexp {^!(.+)$} $name dummy event] {
- return [uplevel [list history redo $event]]
- }
- if [regexp {^\^([^^]*)\^([^^]*)\^?$} $name dummy old new] {
- return [uplevel [list history substitute $old $new]]
+ if [info exists newcmd] {
+ tclLog $newcmd
+ history change $newcmd 0
+ return [uplevel $newcmd]
}
set ret [catch {set cmds [info commands $name*]} msg]
@@ -177,9 +190,11 @@ proc unknown args {
proc auto_load cmd {
global auto_index auto_oldpath auto_path env errorInfo errorCode
- if [info exists auto_index($cmd)] {
- uplevel #0 $auto_index($cmd)
- return [expr {[info commands $cmd] != ""}]
+ foreach name [list $cmd ::$cmd] {
+ if [info exists auto_index($name)] {
+ uplevel #0 $auto_index($name)
+ return [expr {[info commands $name] != ""}]
+ }
}
if ![info exists auto_path] {
return 0
@@ -455,6 +470,10 @@ proc auto_mkindex {dir args} {
proc pkg_mkIndex {dir args} {
global errorCode errorInfo
+ if {[llength $args] == 0} {
+ return -code error "wrong # args: should be\
+ \"pkg_mkIndex dir pattern ?pattern ...?\"";
+ }
append index "# Tcl package index file, version 1.0\n"
append index "# This file is generated by the \"pkg_mkIndex\" command\n"
append index "# and sourced either when an application starts up or\n"
@@ -489,6 +508,13 @@ proc pkg_mkIndex {dir args} {
if [catch {
$c eval {
proc dummy args {}
+ rename package package-orig
+ proc package {what args} {
+ switch -- $what {
+ require { return ; # ignore transitive requires }
+ default { eval package-orig {$what} $args }
+ }
+ }
package unknown dummy
set origCmds [info commands]
set dir "" ;# in case file is pkgIndex.tcl
@@ -514,11 +540,23 @@ proc pkg_mkIndex {dir args} {
source $file
set type source
}
+ foreach ns [namespace children] {
+ namespace import ${ns}::*
+ }
foreach i [info commands] {
set cmds($i) 1
}
foreach i $origCmds {
catch {unset cmds($i)}
+
+ }
+ foreach i [array names cmds] {
+ # reverse engineer which namespace a command comes from
+ set absolute [namespace origin $i]
+ if {[string compare ::$i $absolute] != 0} {
+ set cmds($absolute) 1
+ unset cmds($i)
+ }
}
foreach i [package names] {
if {([string compare [package provide $i] ""] != 0)
@@ -529,7 +567,7 @@ proc pkg_mkIndex {dir args} {
}
}
} msg] {
- puts "error while loading or sourcing $file: $msg"
+ tclLog "error while loading or sourcing $file: $msg"
}
foreach pkg [$c eval set pkgs] {
lappend files($pkg) [list $file [$c eval set type] \
@@ -623,33 +661,37 @@ proc tclPkgUnknown {name version {exact {}}} {
set save_dir $dir
}
for {set i [expr [llength $auto_path] - 1]} {$i >= 0} {incr i -1} {
- foreach file [glob -nocomplain [file join [lindex $auto_path $i] \
- * pkgIndex.tcl]] {
- set dir [file dirname $file]
- if [catch {source $file} msg] {
- puts stderr \
- "error reading package index file $file: $msg"
+ # we can't use glob in safe interps, so enclose the following
+ # in a catch statement
+ catch {
+ foreach file [glob -nocomplain [file join [lindex $auto_path $i] \
+ * pkgIndex.tcl]] {
+ set dir [file dirname $file]
+ if [catch {source $file} msg] {
+ tclLog "error reading package index file $file: $msg"
+ }
}
- }
+ }
set dir [lindex $auto_path $i]
set file [file join $dir pkgIndex.tcl]
- if [file readable $file] {
- if [catch {source $file} msg] {
- puts stderr \
- "error reading package index file $file: $msg"
+ # safe interps usually don't have "file readable", nor stderr channel
+ if {[interp issafe] || [file readable $file]} {
+ if {[catch {source $file} msg] && ![interp issafe]} {
+ tclLog "error reading package index file $file: $msg"
}
}
# On the Macintosh we also look in the resource fork
# of shared libraries
- if {$tcl_platform(platform) == "macintosh"} {
+ # We can't use tclMacPkgSearch in safe interps because it uses glob
+ if {(![interp issafe]) && ($tcl_platform(platform) == "macintosh")} {
set dir [lindex $auto_path $i]
tclMacPkgSearch $dir
- foreach x [glob -nocomplain [file join $dir *]] {
- if [file isdirectory $x] {
- set dir $x
- tclMacPkgSearch $dir
+ foreach x [glob -nocomplain [file join $dir *]] {
+ if [file isdirectory $x] {
+ set dir $x
+ tclMacPkgSearch $dir
+ }
}
- }
}
}
if {[info exists save_dir]} {
diff --git a/contrib/tcl/library/opt0.1/optparse.tcl b/contrib/tcl/library/opt0.1/optparse.tcl
new file mode 100644
index 0000000..ee5b399
--- /dev/null
+++ b/contrib/tcl/library/opt0.1/optparse.tcl
@@ -0,0 +1,1067 @@
+# optparse.tcl --
+#
+# (Private) option parsing package
+#
+# This might be documented and exported in 8.1
+# and some function hopefully moved to the C core for
+# efficiency, if there is enough demand. (mail! ;-)
+#
+# Author: Laurent Demailly - Laurent.Demailly@sun.com - dl@mail.box.eu.org
+#
+# Credits:
+# this is a complete 'over kill' rewrite by me, from a version
+# written initially with Brent Welch, itself initially
+# based on work with Steve Uhler. Thanks them !
+#
+# SCCS: @(#) optparse.tcl 1.11 97/08/11 16:39:15
+
+package provide opt 0.1
+
+namespace eval ::tcl {
+
+ # Exported APIs
+ namespace export OptKeyRegister OptKeyDelete OptKeyError OptKeyParse \
+ OptProc OptProcArgGiven OptParse \
+ Lassign Lvarpop Lvarset Lvarincr Lfirst \
+ SetMax SetMin
+
+
+################# Example of use / 'user documentation' ###################
+
+ proc OptCreateTestProc {} {
+
+ # Defines ::tcl::OptParseTest as a test proc with parsed arguments
+ # (can't be defined before the code below is loaded (before "OptProc"))
+
+ # Every OptProc give usage information on "procname -help".
+ # Try "tcl::OptParseTest -help" and "tcl::OptParseTest -a" and
+ # then other arguments.
+ #
+ # example of 'valid' call:
+ # ::tcl::OptParseTest save -4 -pr 23 -libsok SybTcl\
+ # -nostatics false ch1
+ OptProc OptParseTest {
+ {subcommand -choice {save print} "sub command"}
+ {arg1 3 "some number"}
+ {-aflag}
+ {-intflag 7}
+ {-weirdflag "help string"}
+ {-noStatics "Not ok to load static packages"}
+ {-nestedloading1 true "OK to load into nested slaves"}
+ {-nestedloading2 -boolean true "OK to load into nested slaves"}
+ {-libsOK -choice {Tk SybTcl}
+ "List of packages that can be loaded"}
+ {-precision -int 12 "Number of digits of precision"}
+ {-intval 7 "An integer"}
+ {-scale -float 1.0 "Scale factor"}
+ {-zoom 1.0 "Zoom factor"}
+ {-arbitrary foobar "Arbitrary string"}
+ {-random -string 12 "Random string"}
+ {-listval -list {} "List value"}
+ {-blahflag -blah abc "Funny type"}
+ {arg2 -boolean "a boolean"}
+ {arg3 -choice "ch1 ch2"}
+ {?optarg? -list {} "optional argument"}
+ } {
+ foreach v [info locals] {
+ puts stderr [format "%14s : %s" $v [set $v]]
+ }
+ }
+ }
+
+################### No User serviceable part below ! ###############
+# You should really not look any further :
+# The following is private unexported undocumented unblessed... code
+# time to hit "q" ;-) !
+
+# Hmmm... ok, you really want to know ?
+
+# You've been warned... Here it is...
+
+ # Array storing the parsed descriptions
+ variable OptDesc;
+ array set OptDesc {};
+ # Next potentially free key id (numeric)
+ variable OptDescN 0;
+
+# Inside algorithm/mechanism description:
+# (not for the faint hearted ;-)
+#
+# The argument description is parsed into a "program tree"
+# It is called a "program" because it is the program used by
+# the state machine interpreter that use that program to
+# actually parse the arguments at run time.
+#
+# The general structure of a "program" is
+# notation (pseudo bnf like)
+# name :== definition defines "name" as being "definition"
+# { x y z } means list of x, y, and z
+# x* means x repeated 0 or more time
+# x+ means "x x*"
+# x? means optionally x
+# x | y means x or y
+# "cccc" means the literal string
+#
+# program :== { programCounter programStep* }
+#
+# programStep :== program | singleStep
+#
+# programCounter :== {"P" integer+ }
+#
+# singleStep :== { instruction parameters* }
+#
+# instruction :== single element list
+#
+# (the difference between singleStep and program is that \
+# llength [Lfirst $program] >= 2
+# while
+# llength [Lfirst $singleStep] == 1
+# )
+#
+# And for this application:
+#
+# singleStep :== { instruction varname {hasBeenSet currentValue} type
+# typeArgs help }
+# instruction :== "flags" | "value"
+# type :== knowType | anyword
+# knowType :== "string" | "int" | "boolean" | "boolflag" | "float"
+# | "choice"
+#
+# for type "choice" typeArgs is a list of possible choices, the first one
+# is the default value. for all other types the typeArgs is the default value
+#
+# a "boolflag" is the type for a flag whose presence or absence, without
+# additional arguments means respectively true or false (default flag type).
+#
+# programCounter is the index in the list of the currently processed
+# programStep (thus starting at 1 (0 is {"P" prgCounterValue}).
+# If it is a list it points toward each currently selected programStep.
+# (like for "flags", as they are optional, form a set and programStep).
+
+# Performance/Implementation issues
+# ---------------------------------
+# We use tcl lists instead of arrays because with tcl8.0
+# they should start to be much faster.
+# But this code use a lot of helper procs (like Lvarset)
+# which are quite slow and would be helpfully optimized
+# for instance by being written in C. Also our struture
+# is complex and there is maybe some places where the
+# string rep might be calculated at great exense. to be checked.
+
+#
+# Parse a given description and saves it here under the given key
+# generate a unused keyid if not given
+#
+proc ::tcl::OptKeyRegister {desc {key ""}} {
+ variable OptDesc;
+ variable OptDescN;
+ if {[string compare $key ""] == 0} {
+ # in case a key given to us as a parameter was a number
+ while {[info exists OptDesc($OptDescN)]} {incr OptDescN}
+ set key $OptDescN;
+ incr OptDescN;
+ }
+ # program counter
+ set program [list [list "P" 1]];
+
+ # are we processing flags (which makes a single program step)
+ set inflags 0;
+ set state {};
+
+ foreach item $desc {
+ if {$state == "args"} {
+ # more items after 'args'...
+ return -code error "'args' special argument must be the last one";
+ }
+ set res [OptNormalizeOne $item];
+ set state [Lfirst $res];
+ if {$inflags} {
+ if {$state == "flags"} {
+ # add to 'subprogram'
+ lappend flagsprg $res;
+ } else {
+ # put in the flags
+ # structure for flag programs items is a list of
+ # {subprgcounter {prg flag 1} {prg flag 2} {...}}
+ lappend program $flagsprg;
+ # put the other regular stuff
+ lappend program $res;
+ set inflags 0;
+ }
+ } else {
+ if {$state == "flags"} {
+ set inflags 1;
+ # sub program counter + first sub program
+ set flagsprg [list [list "P" 1] $res];
+ } else {
+ lappend program $res;
+ }
+ }
+ }
+ if {$inflags} {
+ lappend program $flagsprg;
+ }
+
+ set OptDesc($key) $program;
+
+ return $key;
+}
+
+#
+# Free the storage for that given key
+#
+proc ::tcl::OptKeyDelete {key} {
+ variable OptDesc;
+ unset OptDesc($key);
+}
+
+ # Get the parsed description stored under the given key.
+ proc OptKeyGetDesc {descKey} {
+ variable OptDesc;
+ if {![info exists OptDesc($descKey)]} {
+ return -code error "Unknown option description key \"$descKey\"";
+ }
+ set OptDesc($descKey);
+ }
+
+# Parse entry point for ppl who don't want to register with a key,
+# for instance because the description changes dynamically.
+# (otherwise one should really use OptKeyRegister once + OptKeyParse
+# as it is way faster or simply OptProc which does it all)
+# Assign a temporary key, call OptKeyParse and then free the storage
+proc ::tcl::OptParse {desc arglist} {
+ set tempkey [OptKeyRegister $desc];
+ set ret [catch {uplevel [list ::tcl::OptKeyParse $tempkey $arglist]} res];
+ OptKeyDelete $tempkey;
+ return -code $ret $res;
+}
+
+# Helper function, replacement for proc that both
+# register the description under a key which is the name of the proc
+# (and thus unique to that code)
+# and add a first line to the code to call the OptKeyParse proc
+# Stores the list of variables that have been actually given by the user
+# (the other will be sets to their default value)
+# into local variable named "Args".
+proc ::tcl::OptProc {name desc body} {
+ set namespace [uplevel namespace current];
+ if { ([string match $name "::*"])
+ || ([string compare $namespace "::"]==0)} {
+ # absolute name or global namespace, name is the key
+ set key $name;
+ } else {
+ # we are relative to some non top level namespace:
+ set key "${namespace}::${name}";
+ }
+ OptKeyRegister $desc $key;
+ uplevel [list proc $name args "set Args \[::tcl::OptKeyParse $key \$args\]\n$body"];
+ return $key;
+}
+# Check that a argument has been given
+# assumes that "OptProc" has been used as it will check in "Args" list
+proc ::tcl::OptProcArgGiven {argname} {
+ upvar Args alist;
+ expr {[lsearch $alist $argname] >=0}
+}
+
+ #######
+ # Programs/Descriptions manipulation
+
+ # Return the instruction word/list of a given step/(sub)program
+ proc OptInstr {lst} {
+ Lfirst $lst;
+ }
+ # Is a (sub) program or a plain instruction ?
+ proc OptIsPrg {lst} {
+ expr {[llength [OptInstr $lst]]>=2}
+ }
+ # Is this instruction a program counter or a real instr
+ proc OptIsCounter {item} {
+ expr {[Lfirst $item]=="P"}
+ }
+ # Current program counter (2nd word of first word)
+ proc OptGetPrgCounter {lst} {
+ Lget $lst {0 1}
+ }
+ # Current program counter (2nd word of first word)
+ proc OptSetPrgCounter {lstName newValue} {
+ upvar $lstName lst;
+ set lst [lreplace $lst 0 0 [concat "P" $newValue]];
+ }
+ # returns a list of currently selected items.
+ proc OptSelection {lst} {
+ set res {};
+ foreach idx [lrange [Lfirst $lst] 1 end] {
+ lappend res [Lget $lst $idx];
+ }
+ return $res;
+ }
+
+ # Advance to next description
+ proc OptNextDesc {descName} {
+ uplevel [list Lvarincr $descName {0 1}];
+ }
+
+ # Get the current description, eventually descend
+ proc OptCurDesc {descriptions} {
+ lindex $descriptions [OptGetPrgCounter $descriptions];
+ }
+ # get the current description, eventually descend
+ # through sub programs as needed.
+ proc OptCurDescFinal {descriptions} {
+ set item [OptCurDesc $descriptions];
+ # Descend untill we get the actual item and not a sub program
+ while {[OptIsPrg $item]} {
+ set item [OptCurDesc $item];
+ }
+ return $item;
+ }
+ # Current final instruction adress
+ proc OptCurAddr {descriptions {start {}}} {
+ set adress [OptGetPrgCounter $descriptions];
+ lappend start $adress;
+ set item [lindex $descriptions $adress];
+ if {[OptIsPrg $item]} {
+ return [OptCurAddr $item $start];
+ } else {
+ return $start;
+ }
+ }
+ # Set the value field of the current instruction
+ proc OptCurSetValue {descriptionsName value} {
+ upvar $descriptionsName descriptions
+ # get the current item full adress
+ set adress [OptCurAddr $descriptions];
+ # use the 3th field of the item (see OptValue / OptNewInst)
+ lappend adress 2
+ Lvarset descriptions $adress [list 1 $value];
+ # ^hasBeenSet flag
+ }
+
+ # empty state means done/paste the end of the program
+ proc OptState {item} {
+ Lfirst $item
+ }
+
+ # current state
+ proc OptCurState {descriptions} {
+ OptState [OptCurDesc $descriptions];
+ }
+
+ #######
+ # Arguments manipulation
+
+ # Returns the argument that has to be processed now
+ proc OptCurrentArg {lst} {
+ Lfirst $lst;
+ }
+ # Advance to next argument
+ proc OptNextArg {argsName} {
+ uplevel [list Lvarpop $argsName];
+ }
+ #######
+
+
+
+
+
+ # Loop over all descriptions, calling OptDoOne which will
+ # eventually eat all the arguments.
+ proc OptDoAll {descriptionsName argumentsName} {
+ upvar $descriptionsName descriptions
+ upvar $argumentsName arguments;
+# puts "entered DoAll";
+ # Nb: the places where "state" can be set are tricky to figure
+ # because DoOne sets the state to flagsValue and return -continue
+ # when needed...
+ set state [OptCurState $descriptions];
+ # We'll exit the loop in "OptDoOne" or when state is empty.
+ while 1 {
+ set curitem [OptCurDesc $descriptions];
+ # Do subprograms if needed, call ourselves on the sub branch
+ while {[OptIsPrg $curitem]} {
+ OptDoAll curitem arguments
+# puts "done DoAll sub";
+ # Insert back the results in current tree;
+ Lvarset1nc descriptions [OptGetPrgCounter $descriptions]\
+ $curitem;
+ OptNextDesc descriptions;
+ set curitem [OptCurDesc $descriptions];
+ set state [OptCurState $descriptions];
+ }
+# puts "state = \"$state\" - arguments=($arguments)";
+ if {[Lempty $state]} {
+ # Nothing left to do, we are done in this branch:
+ break;
+ }
+ # The following statement can make us terminate/continue
+ # as it use return -code {break, continue, return and error}
+ # codes
+ OptDoOne descriptions state arguments;
+ # If we are here, no special return code where issued,
+ # we'll step to next instruction :
+# puts "new state = \"$state\"";
+ OptNextDesc descriptions;
+ set state [OptCurState $descriptions];
+ }
+ if {![Lempty $arguments]} {
+ return -code error [OptTooManyArgs $descriptions $arguments];
+ }
+ }
+
+ # Process one step for the state machine,
+ # eventually consuming the current argument.
+ proc OptDoOne {descriptionsName stateName argumentsName} {
+ upvar $argumentsName arguments;
+ upvar $descriptionsName descriptions;
+ upvar $stateName state;
+
+ # the special state/instruction "args" eats all
+ # the remaining args (if any)
+ if {($state == "args")} {
+ OptCurSetValue descriptions $arguments;
+ set arguments {};
+# puts "breaking out ('args' state: consuming every reminding args)"
+ return -code break;
+ }
+
+ if {[Lempty $arguments]} {
+ if {$state == "flags"} {
+ # no argument and no flags : we're done
+# puts "returning to previous (sub)prg (no more args)";
+ return -code return;
+ } elseif {$state == "optValue"} {
+ set state next; # not used, for debug only
+ # go to next state
+ return ;
+ } else {
+ return -code error [OptMissingValue $descriptions];
+ }
+ } else {
+ set arg [OptCurrentArg $arguments];
+ }
+
+ switch $state {
+ flags {
+ # A non-dash argument terminates the options, as does --
+
+ # Still a flag ?
+ if {![OptIsFlag $arg]} {
+ # don't consume the argument, return to previous prg
+ return -code return;
+ }
+ # consume the flag
+ OptNextArg arguments;
+ if {[string compare "--" $arg] == 0} {
+ # return from 'flags' state
+ return -code return;
+ }
+
+ set hits [OptHits descriptions $arg];
+ if {$hits > 1} {
+ return -code error [OptAmbigous $descriptions $arg]
+ } elseif {$hits == 0} {
+ return -code error [OptFlagUsage $descriptions $arg]
+ }
+ set item [OptCurDesc $descriptions];
+ if {[OptNeedValue $item]} {
+ # we need a value, next state is
+ set state flagValue;
+ } else {
+ OptCurSetValue descriptions 1;
+ }
+ # continue
+ return -code continue;
+ }
+ flagValue -
+ value {
+ set item [OptCurDesc $descriptions];
+ # Test the values against their required type
+ if [catch {OptCheckType $arg\
+ [OptType $item] [OptTypeArgs $item]} val] {
+ return -code error [OptBadValue $item $arg $val]
+ }
+ # consume the value
+ OptNextArg arguments;
+ # set the value
+ OptCurSetValue descriptions $val;
+ # go to next state
+ if {$state == "flagValue"} {
+ set state flags
+ return -code continue;
+ } else {
+ set state next; # not used, for debug only
+ return ; # will go on next step
+ }
+ }
+ optValue {
+ set item [OptCurDesc $descriptions];
+ # Test the values against their required type
+ if ![catch {OptCheckType $arg\
+ [OptType $item] [OptTypeArgs $item]} val] {
+ # right type, so :
+ # consume the value
+ OptNextArg arguments;
+ # set the value
+ OptCurSetValue descriptions $val;
+ }
+ # go to next state
+ set state next; # not used, for debug only
+ return ; # will go on next step
+ }
+ }
+ # If we reach this point: an unknown
+ # state as been entered !
+ return -code error "Bug! unknown state in DoOne \"$state\"\
+ (prg counter [OptGetPrgCounter $descriptions]:\
+ [OptCurDesc $descriptions])";
+ }
+
+# Parse the options given the key to previously registered description
+# and arguments list
+proc ::tcl::OptKeyParse {descKey arglist} {
+
+ set desc [OptKeyGetDesc $descKey];
+
+ # make sure -help always give usage
+ if {[string compare "-help" [string tolower $arglist]] == 0} {
+ return -code error [OptError "Usage information:" $desc 1];
+ }
+
+ OptDoAll desc arglist;
+
+ # Analyse the result
+ # Walk through the tree:
+ OptTreeVars $desc "#[expr [info level]-1]" ;
+}
+
+ # determine string length for nice tabulated output
+ proc OptTreeVars {desc level {vnamesLst {}}} {
+ foreach item $desc {
+ if {[OptIsCounter $item]} continue;
+ if {[OptIsPrg $item]} {
+ set vnamesLst [OptTreeVars $item $level $vnamesLst];
+ } else {
+ set vname [OptVarName $item];
+ upvar $level $vname var
+ if {[OptHasBeenSet $item]} {
+# puts "adding $vname"
+ # lets use the input name for the returned list
+ # it is more usefull, for instance you can check that
+ # no flags at all was given with expr
+ # {![string match "*-*" $Args]}
+ lappend vnamesLst [OptName $item];
+ set var [OptValue $item];
+ } else {
+ set var [OptDefaultValue $item];
+ }
+ }
+ }
+ return $vnamesLst
+ }
+
+
+# Check the type of a value
+# and emit an error if arg is not of the correct type
+# otherwise returns the canonical value of that arg (ie 0/1 for booleans)
+proc ::tcl::OptCheckType {arg type {typeArgs ""}} {
+# puts "checking '$arg' against '$type' ($typeArgs)";
+
+ # only types "any", "choice", and numbers can have leading "-"
+
+ switch -exact -- $type {
+ int {
+ if ![regexp {^(-+)?[0-9]+$} $arg] {
+ error "not an integer"
+ }
+ return $arg;
+ }
+ float {
+ return [expr double($arg)]
+ }
+ script -
+ list {
+ # if llength fail : malformed list
+ if {[llength $arg]==0} {
+ if {[OptIsFlag $arg]} {
+ error "no values with leading -"
+ }
+ }
+ return $arg;
+ }
+ boolean {
+ if ![regexp -nocase {^(true|false|0|1)$} $arg] {
+ error "non canonic boolean"
+ }
+ # convert true/false because expr/if is broken with "!,...
+ if {$arg} {
+ return 1
+ } else {
+ return 0
+ }
+ }
+ choice {
+ if {[lsearch -exact $typeArgs $arg] < 0} {
+ error "invalid choice"
+ }
+ return $arg;
+ }
+ any {
+ return $arg;
+ }
+ string -
+ default {
+ if {[OptIsFlag $arg]} {
+ error "no values with leading -"
+ }
+ return $arg
+ }
+ }
+ return neverReached;
+}
+
+ # internal utilities
+
+ # returns the number of flags matching the given arg
+ # sets the (local) prg counter to the list of matches
+ proc OptHits {descName arg} {
+ upvar $descName desc;
+ set hits 0
+ set hitems {}
+ set i 1;
+ foreach item [lrange $desc 1 end] {
+ set flag [OptName $item]
+ # lets try to match case insensitively
+ if {[string match [string tolower $arg*] [string tolower $flag]]} {
+ lappend hitems $i;
+ incr hits;
+ }
+ incr i;
+ }
+ if {$hits} {
+ OptSetPrgCounter desc $hitems;
+ }
+ return $hits
+ }
+
+ # Extract fields from the list structure:
+
+ proc OptName {item} {
+ lindex $item 1;
+ }
+ #
+ proc OptHasBeenSet {item} {
+ Lget $item {2 0};
+ }
+ #
+ proc OptValue {item} {
+ Lget $item {2 1};
+ }
+
+ proc OptIsFlag {name} {
+ string match "-*" $name;
+ }
+ proc OptIsOpt {name} {
+ string match {\?*} $name;
+ }
+ proc OptVarName {item} {
+ set name [OptName $item];
+ if {[OptIsFlag $name]} {
+ return [string range $name 1 end];
+ } elseif {[OptIsOpt $name]} {
+ return [string trim $name "?"];
+ } else {
+ return $name;
+ }
+ }
+ proc OptType {item} {
+ lindex $item 3
+ }
+ proc OptTypeArgs {item} {
+ lindex $item 4
+ }
+ proc OptHelp {item} {
+ lindex $item 5
+ }
+ proc OptNeedValue {item} {
+ string compare [OptType $item] boolflag
+ }
+ proc OptDefaultValue {item} {
+ set val [OptTypeArgs $item]
+ switch -exact -- [OptType $item] {
+ choice {return [lindex $val 0]}
+ boolean -
+ boolflag {
+ # convert back false/true to 0/1 because expr !$bool
+ # is broken..
+ if {$val} {
+ return 1
+ } else {
+ return 0
+ }
+ }
+ }
+ return $val
+ }
+
+ # Description format error helper
+ proc OptOptUsage {item {what ""}} {
+ return -code error "invalid description format$what: $item\n\
+ should be a list of {varname|-flagname ?-type? ?defaultvalue?\
+ ?helpstring?}";
+ }
+
+
+ # Generate a canonical form single instruction
+ proc OptNewInst {state varname type typeArgs help} {
+ list $state $varname [list 0 {}] $type $typeArgs $help;
+ # ^ ^
+ # | |
+ # hasBeenSet=+ +=currentValue
+ }
+
+ # Translate one item to canonical form
+ proc OptNormalizeOne {item} {
+ set lg [Lassign $item varname arg1 arg2 arg3];
+# puts "called optnormalizeone '$item' v=($varname), lg=$lg";
+ set isflag [OptIsFlag $varname];
+ set isopt [OptIsOpt $varname];
+ if {$isflag} {
+ set state "flags";
+ } elseif {$isopt} {
+ set state "optValue";
+ } elseif {[string compare $varname "args"]} {
+ set state "value";
+ } else {
+ set state "args";
+ }
+
+ # apply 'smart' 'fuzzy' logic to try to make
+ # description writer's life easy, and our's difficult :
+ # let's guess the missing arguments :-)
+
+ switch $lg {
+ 1 {
+ if {$isflag} {
+ return [OptNewInst $state $varname boolflag false ""];
+ } else {
+ return [OptNewInst $state $varname any "" ""];
+ }
+ }
+ 2 {
+ # varname default
+ # varname help
+ set type [OptGuessType $arg1]
+ if {[string compare $type "string"] == 0} {
+ if {$isflag} {
+ set type boolflag
+ set def false
+ } else {
+ set type any
+ set def ""
+ }
+ set help $arg1
+ } else {
+ set help ""
+ set def $arg1
+ }
+ return [OptNewInst $state $varname $type $def $help];
+ }
+ 3 {
+ # varname type value
+ # varname value comment
+
+ if [regexp {^-(.+)$} $arg1 x type] {
+ # flags/optValue as they are optional, need a "value",
+ # on the contrary, for a variable (non optional),
+ # default value is pointless, 'cept for choices :
+ if {$isflag || $isopt || ($type == "choice")} {
+ return [OptNewInst $state $varname $type $arg2 ""];
+ } else {
+ return [OptNewInst $state $varname $type "" $arg2];
+ }
+ } else {
+ return [OptNewInst $state $varname\
+ [OptGuessType $arg1] $arg1 $arg2]
+ }
+ }
+ 4 {
+ if [regexp {^-(.+)$} $arg1 x type] {
+ return [OptNewInst $state $varname $type $arg2 $arg3];
+ } else {
+ return -code error [OptOptUsage $item];
+ }
+ }
+ default {
+ return -code error [OptOptUsage $item];
+ }
+ }
+ }
+
+ # Auto magic lasy type determination
+ proc OptGuessType {arg} {
+ if [regexp -nocase {^(true|false)$} $arg] {
+ return boolean
+ }
+ if [regexp {^(-+)?[0-9]+$} $arg] {
+ return int
+ }
+ if ![catch {expr double($arg)}] {
+ return float
+ }
+ return string
+ }
+
+ # Error messages front ends
+
+ proc OptAmbigous {desc arg} {
+ OptError "ambigous option \"$arg\", choose from:" [OptSelection $desc]
+ }
+ proc OptFlagUsage {desc arg} {
+ OptError "bad flag \"$arg\", must be one of" $desc;
+ }
+ proc OptTooManyArgs {desc arguments} {
+ OptError "too many arguments (unexpected argument(s): $arguments),\
+ usage:"\
+ $desc 1
+ }
+ proc OptParamType {item} {
+ if {[OptIsFlag $item]} {
+ return "flag";
+ } else {
+ return "parameter";
+ }
+ }
+ proc OptBadValue {item arg {err {}}} {
+# puts "bad val err = \"$err\"";
+ OptError "bad value \"$arg\" for [OptParamType $item]"\
+ [list $item]
+ }
+ proc OptMissingValue {descriptions} {
+# set item [OptCurDescFinal $descriptions];
+ set item [OptCurDesc $descriptions];
+ OptError "no value given for [OptParamType $item] \"[OptName $item]\"\
+ (use -help for full usage) :"\
+ [list $item]
+ }
+
+proc ::tcl::OptKeyError {prefix descKey} {
+ OptError $prefix [OptKeyGetDesc $descKey];
+}
+
+ # determine string length for nice tabulated output
+ proc OptLengths {desc nlName tlName dlName} {
+ upvar $nlName nl;
+ upvar $tlName tl;
+ upvar $dlName dl;
+ foreach item $desc {
+ if {[OptIsCounter $item]} continue;
+ if {[OptIsPrg $item]} {
+ OptLengths $item nl tl dl
+ } else {
+ SetMax nl [string length [OptName $item]]
+ SetMax tl [string length [OptType $item]]
+ set dv [OptTypeArgs $item];
+ if {[OptState $item] != "header"} {
+ set dv "($dv)";
+ }
+ set l [string length $dv];
+ # limit the space allocated to potentially big "choices"
+ if {([OptType $item] != "choice") || ($l<=12)} {
+ SetMax dl $l
+ } else {
+ if {![info exists dl]} {
+ set dl 0
+ }
+ }
+ }
+ }
+ }
+ # output the tree
+ proc OptTree {desc nl tl dl} {
+ set res "";
+ foreach item $desc {
+ if {[OptIsCounter $item]} continue;
+ if {[OptIsPrg $item]} {
+ append res [OptTree $item $nl $tl $dl];
+ } else {
+ set dv [OptTypeArgs $item];
+ if {[OptState $item] != "header"} {
+ set dv "($dv)";
+ }
+ append res [format "\n %-*s %-*s %-*s %s" \
+ $nl [OptName $item] $tl [OptType $item] \
+ $dl $dv [OptHelp $item]]
+ }
+ }
+ return $res;
+ }
+
+# Give nice usage string
+proc ::tcl::OptError {prefix desc {header 0}} {
+ # determine length
+ if {$header} {
+ # add faked instruction
+ set h [list [OptNewInst header Var/FlagName Type Value Help]];
+ lappend h [OptNewInst header ------------ ---- ----- ----];
+ lappend h [OptNewInst header {( -help} "" "" {gives this help )}]
+ set desc [concat $h $desc]
+ }
+ OptLengths $desc nl tl dl
+ # actually output
+ return "$prefix[OptTree $desc $nl $tl $dl]"
+}
+
+
+################ General Utility functions #######################
+
+#
+# List utility functions
+# Naming convention:
+# "Lvarxxx" take the list VARiable name as argument
+# "Lxxxx" take the list value as argument
+# (which is not costly with Tcl8 objects system
+# as it's still a reference and not a copy of the values)
+#
+
+# Is that list empty ?
+proc ::tcl::Lempty {list} {
+ expr {[llength $list]==0}
+}
+
+# Gets the value of one leaf of a lists tree
+proc ::tcl::Lget {list indexLst} {
+ if {[llength $indexLst] <= 1} {
+ return [lindex $list $indexLst];
+ }
+ Lget [lindex $list [Lfirst $indexLst]] [Lrest $indexLst];
+}
+# Sets the value of one leaf of a lists tree
+# (we use the version that does not create the elements because
+# it would be even slower... needs to be written in C !)
+# (nb: there is a non trivial recursive problem with indexes 0,
+# which appear because there is no difference between a list
+# of 1 element and 1 element alone : [list "a"] == "a" while
+# it should be {a} and [listp a] should be 0 while [listp {a b}] would be 1
+# and [listp "a b"] maybe 0. listp does not exist either...)
+proc ::tcl::Lvarset {listName indexLst newValue} {
+ upvar $listName list;
+ if {[llength $indexLst] <= 1} {
+ Lvarset1nc list $indexLst $newValue;
+ } else {
+ set idx [Lfirst $indexLst];
+ set targetList [lindex $list $idx];
+ # reduce refcount on targetList (not really usefull now,
+ # could be with optimizing compiler)
+# Lvarset1 list $idx {};
+ # recursively replace in targetList
+ Lvarset targetList [Lrest $indexLst] $newValue;
+ # put updated sub list back in the tree
+ Lvarset1nc list $idx $targetList;
+ }
+}
+# Set one cell to a value, eventually create all the needed elements
+# (on level-1 of lists)
+variable emptyList {}
+proc ::tcl::Lvarset1 {listName index newValue} {
+ upvar $listName list;
+ if {$index < 0} {return -code error "invalid negative index"}
+ set lg [llength $list];
+ if {$index >= $lg} {
+ variable emptyList;
+ for {set i $lg} {$i<$index} {incr i} {
+ lappend list $emptyList;
+ }
+ lappend list $newValue;
+ } else {
+ set list [lreplace $list $index $index $newValue];
+ }
+}
+# same as Lvarset1 but no bound checking / creation
+proc ::tcl::Lvarset1nc {listName index newValue} {
+ upvar $listName list;
+ set list [lreplace $list $index $index $newValue];
+}
+# Increments the value of one leaf of a lists tree
+# (which must exists)
+proc ::tcl::Lvarincr {listName indexLst {howMuch 1}} {
+ upvar $listName list;
+ if {[llength $indexLst] <= 1} {
+ Lvarincr1 list $indexLst $howMuch;
+ } else {
+ set idx [Lfirst $indexLst];
+ set targetList [lindex $list $idx];
+ # reduce refcount on targetList
+ Lvarset1nc list $idx {};
+ # recursively replace in targetList
+ Lvarincr targetList [Lrest $indexLst] $howMuch;
+ # put updated sub list back in the tree
+ Lvarset1nc list $idx $targetList;
+ }
+}
+# Increments the value of one cell of a list
+proc ::tcl::Lvarincr1 {listName index {howMuch 1}} {
+ upvar $listName list;
+ set newValue [expr [lindex $list $index]+$howMuch];
+ set list [lreplace $list $index $index $newValue];
+ return $newValue;
+}
+# Returns the first element of a list
+proc ::tcl::Lfirst {list} {
+ lindex $list 0
+}
+# Returns the rest of the list minus first element
+proc ::tcl::Lrest {list} {
+ lrange $list 1 end
+}
+# Removes the first element of a list
+proc ::tcl::Lvarpop {listName} {
+ upvar $listName list;
+ set list [lrange $list 1 end];
+}
+# Same but returns the removed element
+proc ::tcl::Lvarpop2 {listName} {
+ upvar $listName list;
+ set el [Lfirst $list];
+ set list [lrange $list 1 end];
+ return $el;
+}
+# Assign list elements to variables and return the length of the list
+proc ::tcl::Lassign {list args} {
+ # faster than direct blown foreach (which does not byte compile)
+ set i 0;
+ set lg [llength $list];
+ foreach vname $args {
+ if {$i>=$lg} break
+ uplevel [list set $vname [lindex $list $i]];
+ incr i;
+ }
+ return $lg;
+}
+
+# Misc utilities
+
+# Set the varname to value if value is greater than varname's current value
+# or if varname is undefined
+proc ::tcl::SetMax {varname value} {
+ upvar 1 $varname var
+ if {![info exists var] || $value > $var} {
+ set var $value
+ }
+}
+
+# Set the varname to value if value is smaller than varname's current value
+# or if varname is undefined
+proc ::tcl::SetMin {varname value} {
+ upvar 1 $varname var
+ if {![info exists var] || $value < $var} {
+ set var $value
+ }
+}
+
+
+ # everything loaded fine, lets create the test proc:
+ OptCreateTestProc
+ # Don't need the create temp proc anymore:
+ rename OptCreateTestProc {}
+}
diff --git a/contrib/tcl/library/opt0.1/pkgIndex.tcl b/contrib/tcl/library/opt0.1/pkgIndex.tcl
new file mode 100644
index 0000000..4e660cd
--- /dev/null
+++ b/contrib/tcl/library/opt0.1/pkgIndex.tcl
@@ -0,0 +1,7 @@
+# Tcl package index file, version 1.0
+# This file is NOT generated by the "pkg_mkIndex" command
+# because if someone just did "package require opt", let's just load
+# the package now, so they can readily use it
+# and even "namespace import tcl::*" ...
+# (tclPkgSetup just makes things slow and do not work so well with namespaces)
+package ifneeded opt 0.1 [list source [file join $dir optparse.tcl]]
diff --git a/contrib/tcl/library/safe.tcl b/contrib/tcl/library/safe.tcl
new file mode 100644
index 0000000..e923cc6
--- /dev/null
+++ b/contrib/tcl/library/safe.tcl
@@ -0,0 +1,710 @@
+# 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.
+#
+# SCCS: @(#) safe.tcl 1.21 97/08/13 15:37:22
+
+#
+# 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 package
+package require opt 0.1;
+
+# Create the safe namespace
+namespace eval ::safe {
+
+ # Exported API:
+ namespace export interp \
+ interpAddToAccessPath interpFindInAccessPath \
+ setLogCmd ;
+
+# Proto/dummy declarations for auto_mkIndex
+proc ::safe::interpCreate {} {}
+proc ::safe::interpInit {} {}
+proc ::safe::interpConfigure {} {}
+proc ::safe::interpDelete {} {}
+
+
+ # Interface/entry point function and front end for "Create"
+ ::tcl::OptProc interpCreate {
+ {?slave? -name {} "name of the slave (optional)"}
+ {-accessPath -list {} "access path for the slave"}
+ {-noStatics "prevent loading of statically linked pkgs"}
+ {-nestedLoadOk "allow nested loading"}
+ {-deleteHook -script {} "delete hook"}
+ } {
+ InterpCreate $slave $accessPath \
+ [expr {!$noStatics}] $nestedLoadOk $deleteHook;
+ }
+
+ # Interface/entry point function and front end for "Init"
+ ::tcl::OptProc interpInit {
+ {slave -name {} "name of the slave"}
+ {-accessPath -list {} "access path for the slave"}
+ {-noStatics "prevent loading of statically linked pkgs"}
+ {-nestedLoadOk "allow nested loading"}
+ {-deleteHook -script {} "delete hook"}
+ } {
+ InterpInit $slave $accessPath \
+ [expr {!$noStatics}] $nestedLoadOk $deleteHook;
+ }
+
+ # Interface/entry point function and front end for "Configure"
+ ::tcl::OptProc interpConfigure {
+ {slave -name {} "name of the slave"}
+ {-accessPath -list {} "access path for the slave"}
+ {-noStatics "prevent loading of statically linked pkgs"}
+ {-nestedLoadOk "allow nested loading"}
+ {-deleteHook -script {} "delete hook"}
+ } {
+ # Check that at least one flag was given:
+ if {[string match "*-*" $Args]} {
+ # reconfigure everything (because otherwise you can't
+ # change -noStatics for instance)
+ InterpConfigure $slave $accessPath \
+ [expr {!$noStatics}] $nestedLoadOk $deleteHook;
+ # auto_reset the slave (to completly synch the new access_path)
+ if {[catch {::interp eval $slave {auto_reset}} msg]} {
+ Log $slave "auto_reset failed: $msg";
+ }
+ } else {
+ # none was given, lets return current values instead
+ set res {}
+ lappend res [list -accessPath [Set [PathListName $slave]]]
+ if {![Set [StaticsOkName $slave]]} {
+ lappend res "-noStatics"
+ }
+ if {[Set [NestedOkName $slave]]} {
+ lappend res "-nestedLoadOk"
+ }
+ lappend res [list -deleteHook [Set [DeleteHookName $slave]]]
+ join $res
+ }
+ }
+
+
+ #
+ # 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 {[string compare "" $slave]} {
+ ::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;
+ }
+
+
+ #
+ # InterpConfigure (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::InterpConfigure {slave access_path staticsok\
+ nestedok deletehook} {
+
+ # determine and store the access path if empty
+ if {[string match "" $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.
+ InterpConfigure $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 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
+
+ # The allowed slave variables already have been set
+ # by Tcl_MakeSafe(3)
+
+
+ # Source init.tcl into the slave, to get auto_load and other
+ # procedures defined:
+
+ # We don't try to use the -rsrc on the mac because it would get
+ # confusing if you would want to customize init.tcl
+ # for a given set of safe slaves, on all the platforms
+ # you just need to give a specific access_path and
+ # the mac should be no exception. As there is no
+ # obvious full "safe ressources" design nor implementation
+ # for the mac, safe interps there will just don't
+ # have that ability. (A specific app can still reenable
+ # that using custom aliases if they want to).
+ # It would also make the security analysis and the Safe Tcl security
+ # model platform dependant and thus more error prone.
+
+ if {[catch {::interp eval $slave\
+ {source [file join $tcl_library init.tcl]}}\
+ msg]} {
+ Log $slave "can't source init.tcl ($msg)";
+ error "can't source init.tcl into slave $slave ($msg)"
+ }
+
+ return $slave
+ }
+
+
+ # Add (only if needed, avoid duplicates) 1 level of
+ # sub directories to an existing path list.
+ # Also removes non directories from the returned list.
+ proc AddSubDirs {pathList} {
+ set res {}
+ foreach dir $pathList {
+ if {[file isdirectory $dir]} {
+ # check that we don't have it yet as a children
+ # of a previous dir
+ if {[lsearch -exact $res $dir]<0} {
+ lappend res $dir;
+ }
+ foreach sub [glob -nocomplain -- [file join $dir *]] {
+ if { ([file isdirectory $sub])
+ && ([lsearch -exact $res $sub]<0) } {
+ # new sub dir, add it !
+ lappend res $sub;
+ }
+ }
+ }
+ }
+ return $res;
+ }
+
+ # This procedure deletes a safe slave managed by Safe Tcl and
+ # cleans up associated state:
+
+ proc ::safe::interpDelete {slave} {
+
+ Log $slave "About to delete" NOTICE;
+
+ # 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
+ set hookname [DeleteHookName $slave];
+ if {[Exists $hookname]} {
+ set hook [Set $hookname];
+ if {![::tcl::Lempty $hook]} {
+ # remove the hook now, otherwise if the hook
+ # calls us somehow, we'll loop
+ Unset $hookname;
+ if {[catch {eval $hook $slave} err]} {
+ Log $slave "Delete hook error ($err)";
+ }
+ }
+ }
+
+ # Discard the global array of state associated with the slave, and
+ # delete the interpreter.
+
+ set statename [InterpStateName $slave];
+ if {[Exists $statename]} {
+ Unset $statename;
+ }
+
+ # if we have been called twice, the interp might have been deleted
+ # already
+ if {[::interp exists $slave]} {
+ ::interp delete $slave;
+ Log $slave "Deleted" NOTICE;
+ }
+
+ return
+ }
+
+ # Set (or get) the loging mecanism
+
+proc ::safe::setLogCmd {args} {
+ variable Log;
+ if {[llength $args] == 0} {
+ return $Log;
+ } else {
+ if {[llength $args] == 1} {
+ set Log [lindex $args 0];
+ } else {
+ set Log $args
+ }
+ }
+}
+
+ # internal variable
+ variable Log {}
+
+ # ------------------- 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.
+ #
+ proc SyncAccessPath {slave} {
+ set slave_auto_path [Set [VirtualPathListName $slave]];
+ ::interp eval $slave [list set auto_path $slave_auto_path];
+ Log $slave \
+ "auto_path in $slave has been set to $slave_auto_path"\
+ NOTICE;
+ ::interp eval $slave [list set tcl_library [lindex $slave_auto_path 0]];
+ }
+
+ # base name for storing all the slave states
+ # the array variable name for slave foo is thus "Sfoo"
+ # and for sub slave {foo bar} "Sfoo bar" (spaces are handled
+ # ok everywhere (or should))
+ # We add the S prefix to avoid that a slave interp called Log
+ # would smash our Log variable.
+ proc InterpStateName {slave} {
+ return "S$slave";
+ }
+
+ # returns the virtual token for directory number N
+ # if the slave argument is given,
+ # it will return the corresponding master global variable name
+ proc PathToken {n {slave ""}} {
+ if {[string compare "" $slave]} {
+ return "[InterpStateName $slave](access_path,$n)";
+ } else {
+ # We need to have a ":" in the token string so
+ # [file join] on the mac won't turn it into a relative
+ # path.
+ return "p(:$n:)";
+ }
+ }
+ # returns the variable name of the complete path list
+ proc PathListName {slave} {
+ return "[InterpStateName $slave](access_path)";
+ }
+ # returns the variable name of the complete path list
+ proc VirtualPathListName {slave} {
+ return "[InterpStateName $slave](access_path_slave)";
+ }
+ # returns the variable name of the number of items
+ proc PathNumberName {slave} {
+ return "[InterpStateName $slave](access_path,n)";
+ }
+ # returns the staticsok flag var name
+ proc StaticsOkName {slave} {
+ return "[InterpStateName $slave](staticsok)";
+ }
+ # returns the nestedok flag var name
+ proc NestedOkName {slave} {
+ return "[InterpStateName $slave](nestedok)";
+ }
+ # Run some code at the namespace toplevel
+ proc Toplevel {args} {
+ namespace eval [namespace current] $args;
+ }
+ # set/get values
+ proc Set {args} {
+ eval Toplevel set $args;
+ }
+ # lappend on toplevel vars
+ proc Lappend {args} {
+ eval Toplevel lappend $args;
+ }
+ # unset a var/token (currently just an global level eval)
+ proc Unset {args} {
+ eval Toplevel unset $args;
+ }
+ # test existance
+ proc Exists {varname} {
+ Toplevel info exists $varname;
+ }
+ # short cut for access path getting
+ proc GetAccessPath {slave} {
+ Set [PathListName $slave]
+ }
+ # short cut for statics ok flag getting
+ proc StaticsOk {slave} {
+ Set [StaticsOkName $slave]
+ }
+ # short cut for getting the multiples interps sub loading ok flag
+ proc NestedOk {slave} {
+ Set [NestedOkName $slave]
+ }
+ # interp deletion storing hook name
+ proc DeleteHookName {slave} {
+ return [InterpStateName $slave](cleanupHook)
+ }
+
+ #
+ # translate virtual path into real path
+ #
+ proc TranslatePath {slave path} {
+ # somehow strip the namespaces 'functionality' out (the danger
+ # is that we would strip valid macintosh "../" queries... :
+ if {[regexp {(::)|(\.\.)} $path]} {
+ error "invalid characters in path $path";
+ }
+ set n [expr [Set [PathNumberName $slave]]-1];
+ for {} {$n>=0} {incr n -1} {
+ # fill the token virtual names with their real value
+ set [PathToken $n] [Set [PathToken $n $slave]];
+ }
+ # replaces the token by their value
+ subst -nobackslashes -nocommands $path;
+ }
+
+
+ # Log eventually log an error
+ # to enable error logging, set Log to {puts stderr} for instance
+ proc Log {slave msg {type ERROR}} {
+ variable Log;
+ if {[info exists Log] && [llength $Log]} {
+ eval $Log [list "$type for slave $slave : $msg"];
+ }
+ }
+
+
+ # file name control (limit access to files/ressources that should be
+ # a valid tcl source file)
+ proc CheckFileName {slave file} {
+ # limit what can be sourced to .tcl
+ # and forbid files with more than 1 dot and
+ # longer than 14 chars
+ set ftail [file tail $file];
+ if {[string length $ftail]>14} {
+ error "$ftail: filename too long";
+ }
+ if {[regexp {\..*\.} $ftail]} {
+ error "$ftail: more than one dot is forbidden";
+ }
+ if {[string compare $ftail "tclIndex"] && \
+ [string compare [string tolower [file extension $ftail]]\
+ ".tcl"]} {
+ error "$ftail: must be a *.tcl or tclIndex";
+ }
+
+ if {![file exists $file]} {
+ # don't tell the file path
+ error "no such file or directory";
+ }
+
+ if {![file readable $file]} {
+ # don't tell the file path
+ error "not readable";
+ }
+
+ }
+
+
+ # AliasSource is the target of the "source" alias in safe interpreters.
+
+ proc AliasSource {slave args} {
+
+ set argc [llength $args];
+ # Allow only "source filename"
+ # (and not mac specific -rsrc for instance - see comment in ::init
+ # for current rationale)
+ if {$argc != 1} {
+ set msg "wrong # args: should be \"source fileName\""
+ Log $slave "$msg ($args)";
+ return -code error $msg;
+ }
+ set file [lindex $args 0]
+
+ # get the real path from the virtual one.
+ if {[catch {set file [TranslatePath $slave $file]} msg]} {
+ Log $slave $msg;
+ return -code error "permission denied"
+ }
+
+ # check that the path is in the access path of that slave
+ if {[catch {FileInAccessPath $slave $file} msg]} {
+ Log $slave $msg;
+ return -code error "permission denied"
+ }
+
+ # do the checks on the filename :
+ if {[catch {CheckFileName $slave $file} msg]} {
+ Log $slave "$file:$msg";
+ return -code error $msg;
+ }
+
+ # passed all the tests , lets source it:
+ if {[catch {::interp invokehidden $slave source $file} msg]} {
+ Log $slave $msg;
+ return -code error "script error";
+ }
+ return $msg
+ }
+
+ # AliasLoad is the target of the "load" alias in safe interpreters.
+
+ proc AliasLoad {slave file args} {
+
+ set argc [llength $args];
+ if {$argc > 2} {
+ set msg "load error: too many arguments";
+ Log $slave "$msg ($argc) {$file $args}";
+ return -code error $msg;
+ }
+
+ # package name (can be empty if file is not).
+ set package [lindex $args 0];
+
+ # Determine where to load. load use a relative interp path
+ # and {} means self, so we can directly and safely use passed arg.
+ set target [lindex $args 1];
+ if {[string length $target]} {
+ # we will try to load into a sub sub interp
+ # check that we want to authorize that.
+ if {![NestedOk $slave]} {
+ Log $slave "loading to a sub interp (nestedok)\
+ disabled (trying to load $package to $target)";
+ return -code error "permission denied (nested load)";
+ }
+
+ }
+
+ # Determine what kind of load is requested
+ if {[string length $file] == 0} {
+ # static package loading
+ if {[string length $package] == 0} {
+ set msg "load error: empty filename and no package name";
+ Log $slave $msg;
+ return -code error $msg;
+ }
+ if {![StaticsOk $slave]} {
+ Log $slave "static packages loading disabled\
+ (trying to load $package to $target)";
+ return -code error "permission denied (static package)";
+ }
+ } else {
+ # file loading
+
+ # get the real path from the virtual one.
+ if {[catch {set file [TranslatePath $slave $file]} msg]} {
+ Log $slave $msg;
+ return -code error "permission denied"
+ }
+
+ # check the translated path
+ if {[catch {FileInAccessPath $slave $file} msg]} {
+ Log $slave $msg;
+ return -code error "permission denied (path)"
+ }
+ }
+
+ if {[catch {::interp invokehidden\
+ $slave load $file $package $target} msg]} {
+ Log $slave $msg;
+ return -code error $msg
+ }
+
+ return $msg
+ }
+
+ # FileInAccessPath raises an error if the file is not found in
+ # the list of directories contained in the (master side recorded) slave's
+ # access path.
+
+ # the security here relies on "file dirname" answering the proper
+ # result.... needs checking ?
+ proc FileInAccessPath {slave file} {
+
+ set access_path [GetAccessPath $slave];
+
+ if {[file isdirectory $file]} {
+ error "\"$file\": is a directory"
+ }
+ set parent [file dirname $file]
+ if {[lsearch -exact $access_path $parent] == -1} {
+ error "\"$file\": not in access_path";
+ }
+ }
+
+ # This procedure enables access from a safe interpreter to only a subset of
+ # the subcommands of a command:
+
+ proc Subset {slave command okpat args} {
+ set subcommand [lindex $args 0]
+ if {[regexp $okpat $subcommand]} {
+ return [eval {$command $subcommand} [lrange $args 1 end]]
+ }
+ set msg "not allowed to invoke subcommand $subcommand of $command";
+ Log $slave $msg;
+ error $msg;
+ }
+
+ # This procedure installs an alias in a slave that invokes "safesubset"
+ # in the master to execute allowed subcommands. It precomputes the pattern
+ # of allowed subcommands; you can use wildcards in the pattern if you wish
+ # to allow subcommand abbreviation.
+ #
+ # Syntax is: AliasSubset slave alias target subcommand1 subcommand2...
+
+ proc AliasSubset {slave alias target args} {
+ set pat ^(; set sep ""
+ foreach sub $args {
+ append pat $sep$sub
+ set sep |
+ }
+ append pat )\$
+ ::interp alias $slave $alias {}\
+ [namespace current]::Subset $slave $target $pat
+ }
+
+}
diff --git a/contrib/tcl/library/tclIndex b/contrib/tcl/library/tclIndex
index a0acc86..7ef9563 100644
--- a/contrib/tcl/library/tclIndex
+++ b/contrib/tcl/library/tclIndex
@@ -6,28 +6,6 @@
# element name is the name of a command and the value is
# a script that loads the command.
-set auto_index(parray) [list source [file join $dir parray.tcl]]
-set auto_index(tclLdAout) [list source [file join $dir ldAout.tcl]]
-set auto_index(tcl_wordBreakAfter) [list source [file join $dir word.tcl]]
-set auto_index(tcl_wordBreakBefore) [list source [file join $dir word.tcl]]
-set auto_index(tcl_endOfWord) [list source [file join $dir word.tcl]]
-set auto_index(tcl_startOfNextWord) [list source [file join $dir word.tcl]]
-set auto_index(tcl_startOfPreviousWord) [list source [file join $dir word.tcl]]
-set auto_index(tcl_safeCreateInterp) [list source [file join $dir safeinit.tcl]]
-set auto_index(tcl_safeInitInterp) [list source [file join $dir safeinit.tcl]]
-set auto_index(tcl_safeDeleteInterp) [list source [file join $dir safeinit.tcl]]
-set auto_index(tclSafeComputePolicyPath) [list source [file join $dir safeinit.tcl]]
-set auto_index(tclSafeAliasSource) [list source [file join $dir safeinit.tcl]]
-set auto_index(tclSafeAliasLoad) [list source [file join $dir safeinit.tcl]]
-set auto_index(tclFileInPath) [list source [file join $dir safeinit.tcl]]
-set auto_index(tclSafeCheckAutoPath) [list source [file join $dir safeinit.tcl]]
-set auto_index(tclSafeAliasPkgUnknown) [list source [file join $dir safeinit.tcl]]
-set auto_index(tclSafeLoadPkg) [list source [file join $dir safeinit.tcl]]
-set auto_index(tclSafeLoadPkgInternal) [list source [file join $dir safeinit.tcl]]
-set auto_index(tclSafeResearchPolicyPath) [list source [file join $dir safeinit.tcl]]
-set auto_index(tclSafeLoadPolicy) [list source [file join $dir safeinit.tcl]]
-set auto_index(tclSafeSubset) [list source [file join $dir safeinit.tcl]]
-set auto_index(tclAliasSubset) [list source [file join $dir safeinit.tcl]]
set auto_index(unknown) [list source [file join $dir init.tcl]]
set auto_index(auto_load) [list source [file join $dir init.tcl]]
set auto_index(auto_execok) [list source [file join $dir init.tcl]]
@@ -38,3 +16,18 @@ set auto_index(pkg_mkIndex) [list source [file join $dir init.tcl]]
set auto_index(tclPkgSetup) [list source [file join $dir init.tcl]]
set auto_index(tclMacPkgSearch) [list source [file join $dir init.tcl]]
set auto_index(tclPkgUnknown) [list source [file join $dir init.tcl]]
+set auto_index(parray) [list source [file join $dir parray.tcl]]
+set auto_index(tclLdAout) [list source [file join $dir ldAout.tcl]]
+set auto_index(tcl_wordBreakAfter) [list source [file join $dir word.tcl]]
+set auto_index(tcl_wordBreakBefore) [list source [file join $dir word.tcl]]
+set auto_index(tcl_endOfWord) [list source [file join $dir word.tcl]]
+set auto_index(tcl_startOfNextWord) [list source [file join $dir word.tcl]]
+set auto_index(tcl_startOfPreviousWord) [list source [file join $dir word.tcl]]
+set auto_index(::safe::interpCreate) [list source [file join $dir safe.tcl]]
+set auto_index(::safe::interpInit) [list source [file join $dir safe.tcl]]
+set auto_index(::safe::interpConfigure) [list source [file join $dir safe.tcl]]
+set auto_index(::safe::interpDelete) [list source [file join $dir safe.tcl]]
+set auto_index(::safe::interpFindInAccessPath) [list source [file join $dir safe.tcl]]
+set auto_index(::safe::interpAddToAccessPath) [list source [file join $dir safe.tcl]]
+set auto_index(::safe::setLogCmd) [list source [file join $dir safe.tcl]]
+set auto_index(history) [list source [file join $dir history.tcl]]
OpenPOWER on IntegriCloud