jpayne@68: # init.tcl -- jpayne@68: # jpayne@68: # Default system startup file for Tcl-based applications. Defines jpayne@68: # "unknown" procedure and auto-load facilities. jpayne@68: # jpayne@68: # Copyright (c) 1991-1993 The Regents of the University of California. jpayne@68: # Copyright (c) 1994-1996 Sun Microsystems, Inc. jpayne@68: # Copyright (c) 1998-1999 Scriptics Corporation. jpayne@68: # Copyright (c) 2004 Kevin B. Kenny. All rights reserved. jpayne@68: # jpayne@68: # See the file "license.terms" for information on usage and redistribution jpayne@68: # of this file, and for a DISCLAIMER OF ALL WARRANTIES. jpayne@68: # jpayne@68: jpayne@68: # This test intentionally written in pre-7.5 Tcl jpayne@68: if {[info commands package] == ""} { jpayne@68: error "version mismatch: library\nscripts expect Tcl version 7.5b1 or later but the loaded version is\nonly [info patchlevel]" jpayne@68: } jpayne@68: package require -exact Tcl 8.6.13 jpayne@68: jpayne@68: # Compute the auto path to use in this interpreter. jpayne@68: # The values on the path come from several locations: jpayne@68: # jpayne@68: # The environment variable TCLLIBPATH jpayne@68: # jpayne@68: # tcl_library, which is the directory containing this init.tcl script. jpayne@68: # [tclInit] (Tcl_Init()) searches around for the directory containing this jpayne@68: # init.tcl and defines tcl_library to that location before sourcing it. jpayne@68: # jpayne@68: # The parent directory of tcl_library. Adding the parent jpayne@68: # means that packages in peer directories will be found automatically. jpayne@68: # jpayne@68: # Also add the directory ../lib relative to the directory where the jpayne@68: # executable is located. This is meant to find binary packages for the jpayne@68: # same architecture as the current executable. jpayne@68: # jpayne@68: # tcl_pkgPath, which is set by the platform-specific initialization routines jpayne@68: # On UNIX it is compiled in jpayne@68: # On Windows, it is not used jpayne@68: # jpayne@68: # (Ticket 41c9857bdd) In a safe interpreter, this file does not set jpayne@68: # ::auto_path (other than to {} if it is undefined). The caller, typically jpayne@68: # a Safe Base command, is responsible for setting ::auto_path. jpayne@68: jpayne@68: if {![info exists auto_path]} { jpayne@68: if {[info exists env(TCLLIBPATH)] && (![interp issafe])} { jpayne@68: set auto_path $env(TCLLIBPATH) jpayne@68: } else { jpayne@68: set auto_path "" jpayne@68: } jpayne@68: } jpayne@68: namespace eval tcl { jpayne@68: if {![interp issafe]} { jpayne@68: variable Dir jpayne@68: foreach Dir [list $::tcl_library [file dirname $::tcl_library]] { jpayne@68: if {$Dir ni $::auto_path} { jpayne@68: lappend ::auto_path $Dir jpayne@68: } jpayne@68: } jpayne@68: set Dir [file join [file dirname [file dirname \ jpayne@68: [info nameofexecutable]]] lib] jpayne@68: if {$Dir ni $::auto_path} { jpayne@68: lappend ::auto_path $Dir jpayne@68: } jpayne@68: if {[info exists ::tcl_pkgPath]} { catch { jpayne@68: foreach Dir $::tcl_pkgPath { jpayne@68: if {$Dir ni $::auto_path} { jpayne@68: lappend ::auto_path $Dir jpayne@68: } jpayne@68: } jpayne@68: }} jpayne@68: jpayne@68: variable Path [encoding dirs] jpayne@68: set Dir [file join $::tcl_library encoding] jpayne@68: if {$Dir ni $Path} { jpayne@68: lappend Path $Dir jpayne@68: encoding dirs $Path jpayne@68: } jpayne@68: unset Dir Path jpayne@68: } jpayne@68: jpayne@68: # TIP #255 min and max functions jpayne@68: namespace eval mathfunc { jpayne@68: proc min {args} { jpayne@68: if {![llength $args]} { jpayne@68: return -code error \ jpayne@68: "not enough arguments to math function \"min\"" jpayne@68: } jpayne@68: set val Inf jpayne@68: foreach arg $args { jpayne@68: # This will handle forcing the numeric value without jpayne@68: # ruining the internal type of a numeric object jpayne@68: if {[catch {expr {double($arg)}} err]} { jpayne@68: return -code error $err jpayne@68: } jpayne@68: if {$arg < $val} {set val $arg} jpayne@68: } jpayne@68: return $val jpayne@68: } jpayne@68: proc max {args} { jpayne@68: if {![llength $args]} { jpayne@68: return -code error \ jpayne@68: "not enough arguments to math function \"max\"" jpayne@68: } jpayne@68: set val -Inf jpayne@68: foreach arg $args { jpayne@68: # This will handle forcing the numeric value without jpayne@68: # ruining the internal type of a numeric object jpayne@68: if {[catch {expr {double($arg)}} err]} { jpayne@68: return -code error $err jpayne@68: } jpayne@68: if {$arg > $val} {set val $arg} jpayne@68: } jpayne@68: return $val jpayne@68: } jpayne@68: namespace export min max jpayne@68: } jpayne@68: } jpayne@68: jpayne@68: # Windows specific end of initialization jpayne@68: jpayne@68: if {(![interp issafe]) && ($tcl_platform(platform) eq "windows")} { jpayne@68: namespace eval tcl { jpayne@68: proc EnvTraceProc {lo n1 n2 op} { jpayne@68: global env jpayne@68: set x $env($n2) jpayne@68: set env($lo) $x jpayne@68: set env([string toupper $lo]) $x jpayne@68: } jpayne@68: proc InitWinEnv {} { jpayne@68: global env tcl_platform jpayne@68: foreach p [array names env] { jpayne@68: set u [string toupper $p] jpayne@68: if {$u ne $p} { jpayne@68: switch -- $u { jpayne@68: COMSPEC - jpayne@68: PATH { jpayne@68: set temp $env($p) jpayne@68: unset env($p) jpayne@68: set env($u) $temp jpayne@68: trace add variable env($p) write \ jpayne@68: [namespace code [list EnvTraceProc $p]] jpayne@68: trace add variable env($u) write \ jpayne@68: [namespace code [list EnvTraceProc $p]] jpayne@68: } jpayne@68: } jpayne@68: } jpayne@68: } jpayne@68: if {![info exists env(COMSPEC)]} { jpayne@68: set env(COMSPEC) cmd.exe jpayne@68: } jpayne@68: } jpayne@68: InitWinEnv jpayne@68: } jpayne@68: } jpayne@68: jpayne@68: # Setup the unknown package handler jpayne@68: jpayne@68: jpayne@68: if {[interp issafe]} { jpayne@68: package unknown {::tcl::tm::UnknownHandler ::tclPkgUnknown} jpayne@68: } else { jpayne@68: # Set up search for Tcl Modules (TIP #189). jpayne@68: # and setup platform specific unknown package handlers jpayne@68: if {$tcl_platform(os) eq "Darwin" jpayne@68: && $tcl_platform(platform) eq "unix"} { jpayne@68: package unknown {::tcl::tm::UnknownHandler \ jpayne@68: {::tcl::MacOSXPkgUnknown ::tclPkgUnknown}} jpayne@68: } else { jpayne@68: package unknown {::tcl::tm::UnknownHandler ::tclPkgUnknown} jpayne@68: } jpayne@68: jpayne@68: # Set up the 'clock' ensemble jpayne@68: jpayne@68: namespace eval ::tcl::clock [list variable TclLibDir $::tcl_library] jpayne@68: jpayne@68: proc ::tcl::initClock {} { jpayne@68: # Auto-loading stubs for 'clock.tcl' jpayne@68: jpayne@68: foreach cmd {add format scan} { jpayne@68: proc ::tcl::clock::$cmd args { jpayne@68: variable TclLibDir jpayne@68: source -encoding utf-8 [file join $TclLibDir clock.tcl] jpayne@68: return [uplevel 1 [info level 0]] jpayne@68: } jpayne@68: } jpayne@68: jpayne@68: rename ::tcl::initClock {} jpayne@68: } jpayne@68: ::tcl::initClock jpayne@68: } jpayne@68: jpayne@68: # Conditionalize for presence of exec. jpayne@68: jpayne@68: if {[namespace which -command exec] eq ""} { jpayne@68: jpayne@68: # Some machines do not have exec. Also, on all jpayne@68: # platforms, safe interpreters do not have exec. jpayne@68: jpayne@68: set auto_noexec 1 jpayne@68: } jpayne@68: jpayne@68: # Define a log command (which can be overwitten to log errors jpayne@68: # differently, specially when stderr is not available) jpayne@68: jpayne@68: if {[namespace which -command tclLog] eq ""} { jpayne@68: proc tclLog {string} { jpayne@68: catch {puts stderr $string} jpayne@68: } jpayne@68: } jpayne@68: jpayne@68: # unknown -- jpayne@68: # This procedure is called when a Tcl command is invoked that doesn't jpayne@68: # exist in the interpreter. It takes the following steps to make the jpayne@68: # command available: jpayne@68: # jpayne@68: # 1. See if the autoload facility can locate the command in a jpayne@68: # Tcl script file. If so, load it and execute it. jpayne@68: # 2. If the command was invoked interactively at top-level: jpayne@68: # (a) see if the command exists as an executable UNIX program. jpayne@68: # If so, "exec" the command. jpayne@68: # (b) see if the command requests csh-like history substitution jpayne@68: # in one of the common forms !!, !, or ^old^new. If jpayne@68: # so, emulate csh's history substitution. jpayne@68: # (c) see if the command is a unique abbreviation for another jpayne@68: # command. If so, invoke the command. jpayne@68: # jpayne@68: # Arguments: jpayne@68: # args - A list whose elements are the words of the original jpayne@68: # command, including the command name. jpayne@68: jpayne@68: proc unknown args { jpayne@68: variable ::tcl::UnknownPending jpayne@68: global auto_noexec auto_noload env tcl_interactive errorInfo errorCode jpayne@68: jpayne@68: if {[info exists errorInfo]} { jpayne@68: set savedErrorInfo $errorInfo jpayne@68: } jpayne@68: if {[info exists errorCode]} { jpayne@68: set savedErrorCode $errorCode jpayne@68: } jpayne@68: jpayne@68: set name [lindex $args 0] jpayne@68: if {![info exists auto_noload]} { jpayne@68: # jpayne@68: # Make sure we're not trying to load the same proc twice. jpayne@68: # jpayne@68: if {[info exists UnknownPending($name)]} { jpayne@68: return -code error "self-referential recursion\ jpayne@68: in \"unknown\" for command \"$name\"" jpayne@68: } jpayne@68: set UnknownPending($name) pending jpayne@68: set ret [catch { jpayne@68: auto_load $name [uplevel 1 {::namespace current}] jpayne@68: } msg opts] jpayne@68: unset UnknownPending($name) jpayne@68: if {$ret != 0} { jpayne@68: dict append opts -errorinfo "\n (autoloading \"$name\")" jpayne@68: return -options $opts $msg jpayne@68: } jpayne@68: if {![array size UnknownPending]} { jpayne@68: unset UnknownPending jpayne@68: } jpayne@68: if {$msg} { jpayne@68: if {[info exists savedErrorCode]} { jpayne@68: set ::errorCode $savedErrorCode jpayne@68: } else { jpayne@68: unset -nocomplain ::errorCode jpayne@68: } jpayne@68: if {[info exists savedErrorInfo]} { jpayne@68: set errorInfo $savedErrorInfo jpayne@68: } else { jpayne@68: unset -nocomplain errorInfo jpayne@68: } jpayne@68: set code [catch {uplevel 1 $args} msg opts] jpayne@68: if {$code == 1} { jpayne@68: # jpayne@68: # Compute stack trace contribution from the [uplevel]. jpayne@68: # Note the dependence on how Tcl_AddErrorInfo, etc. jpayne@68: # construct the stack trace. jpayne@68: # jpayne@68: set errInfo [dict get $opts -errorinfo] jpayne@68: set errCode [dict get $opts -errorcode] jpayne@68: set cinfo $args jpayne@68: if {[string bytelength $cinfo] > 150} { jpayne@68: set cinfo [string range $cinfo 0 150] jpayne@68: while {[string bytelength $cinfo] > 150} { jpayne@68: set cinfo [string range $cinfo 0 end-1] jpayne@68: } jpayne@68: append cinfo ... jpayne@68: } jpayne@68: set tail "\n (\"uplevel\" body line 1)\n invoked\ jpayne@68: from within\n\"uplevel 1 \$args\"" jpayne@68: set expect "$msg\n while executing\n\"$cinfo\"$tail" jpayne@68: if {$errInfo eq $expect} { jpayne@68: # jpayne@68: # The stack has only the eval from the expanded command jpayne@68: # Do not generate any stack trace here. jpayne@68: # jpayne@68: dict unset opts -errorinfo jpayne@68: dict incr opts -level jpayne@68: return -options $opts $msg jpayne@68: } jpayne@68: # jpayne@68: # Stack trace is nested, trim off just the contribution jpayne@68: # from the extra "eval" of $args due to the "catch" above. jpayne@68: # jpayne@68: set last [string last $tail $errInfo] jpayne@68: if {$last + [string length $tail] != [string length $errInfo]} { jpayne@68: # Very likely cannot happen jpayne@68: return -options $opts $msg jpayne@68: } jpayne@68: set errInfo [string range $errInfo 0 $last-1] jpayne@68: set tail "\"$cinfo\"" jpayne@68: set last [string last $tail $errInfo] jpayne@68: if {$last < 0 || $last + [string length $tail] != [string length $errInfo]} { jpayne@68: return -code error -errorcode $errCode \ jpayne@68: -errorinfo $errInfo $msg jpayne@68: } jpayne@68: set errInfo [string range $errInfo 0 $last-1] jpayne@68: set tail "\n invoked from within\n" jpayne@68: set last [string last $tail $errInfo] jpayne@68: if {$last + [string length $tail] == [string length $errInfo]} { jpayne@68: return -code error -errorcode $errCode \ jpayne@68: -errorinfo [string range $errInfo 0 $last-1] $msg jpayne@68: } jpayne@68: set tail "\n while executing\n" jpayne@68: set last [string last $tail $errInfo] jpayne@68: if {$last + [string length $tail] == [string length $errInfo]} { jpayne@68: return -code error -errorcode $errCode \ jpayne@68: -errorinfo [string range $errInfo 0 $last-1] $msg jpayne@68: } jpayne@68: return -options $opts $msg jpayne@68: } else { jpayne@68: dict incr opts -level jpayne@68: return -options $opts $msg jpayne@68: } jpayne@68: } jpayne@68: } jpayne@68: jpayne@68: if {([info level] == 1) && ([info script] eq "") jpayne@68: && [info exists tcl_interactive] && $tcl_interactive} { jpayne@68: if {![info exists auto_noexec]} { jpayne@68: set new [auto_execok $name] jpayne@68: if {$new ne ""} { jpayne@68: set redir "" jpayne@68: if {[namespace which -command console] eq ""} { jpayne@68: set redir ">&@stdout <@stdin" jpayne@68: } jpayne@68: uplevel 1 [list ::catch \ jpayne@68: [concat exec $redir $new [lrange $args 1 end]] \ jpayne@68: ::tcl::UnknownResult ::tcl::UnknownOptions] jpayne@68: dict incr ::tcl::UnknownOptions -level jpayne@68: return -options $::tcl::UnknownOptions $::tcl::UnknownResult jpayne@68: } jpayne@68: } jpayne@68: if {$name eq "!!"} { jpayne@68: set newcmd [history event] jpayne@68: } elseif {[regexp {^!(.+)$} $name -> event]} { jpayne@68: set newcmd [history event $event] jpayne@68: } elseif {[regexp {^\^([^^]*)\^([^^]*)\^?$} $name -> old new]} { jpayne@68: set newcmd [history event -1] jpayne@68: catch {regsub -all -- $old $newcmd $new newcmd} jpayne@68: } jpayne@68: if {[info exists newcmd]} { jpayne@68: tclLog $newcmd jpayne@68: history change $newcmd 0 jpayne@68: uplevel 1 [list ::catch $newcmd \ jpayne@68: ::tcl::UnknownResult ::tcl::UnknownOptions] jpayne@68: dict incr ::tcl::UnknownOptions -level jpayne@68: return -options $::tcl::UnknownOptions $::tcl::UnknownResult jpayne@68: } jpayne@68: jpayne@68: set ret [catch {set candidates [info commands $name*]} msg] jpayne@68: if {$name eq "::"} { jpayne@68: set name "" jpayne@68: } jpayne@68: if {$ret != 0} { jpayne@68: dict append opts -errorinfo \ jpayne@68: "\n (expanding command prefix \"$name\" in unknown)" jpayne@68: return -options $opts $msg jpayne@68: } jpayne@68: # Filter out bogus matches when $name contained jpayne@68: # a glob-special char [Bug 946952] jpayne@68: if {$name eq ""} { jpayne@68: # Handle empty $name separately due to strangeness jpayne@68: # in [string first] (See RFE 1243354) jpayne@68: set cmds $candidates jpayne@68: } else { jpayne@68: set cmds [list] jpayne@68: foreach x $candidates { jpayne@68: if {[string first $name $x] == 0} { jpayne@68: lappend cmds $x jpayne@68: } jpayne@68: } jpayne@68: } jpayne@68: if {[llength $cmds] == 1} { jpayne@68: uplevel 1 [list ::catch [lreplace $args 0 0 [lindex $cmds 0]] \ jpayne@68: ::tcl::UnknownResult ::tcl::UnknownOptions] jpayne@68: dict incr ::tcl::UnknownOptions -level jpayne@68: return -options $::tcl::UnknownOptions $::tcl::UnknownResult jpayne@68: } jpayne@68: if {[llength $cmds]} { jpayne@68: return -code error "ambiguous command name \"$name\": [lsort $cmds]" jpayne@68: } jpayne@68: } jpayne@68: return -code error -errorcode [list TCL LOOKUP COMMAND $name] \ jpayne@68: "invalid command name \"$name\"" jpayne@68: } jpayne@68: jpayne@68: # auto_load -- jpayne@68: # Checks a collection of library directories to see if a procedure jpayne@68: # is defined in one of them. If so, it sources the appropriate jpayne@68: # library file to create the procedure. Returns 1 if it successfully jpayne@68: # loaded the procedure, 0 otherwise. jpayne@68: # jpayne@68: # Arguments: jpayne@68: # cmd - Name of the command to find and load. jpayne@68: # namespace (optional) The namespace where the command is being used - must be jpayne@68: # a canonical namespace as returned [namespace current] jpayne@68: # for instance. If not given, namespace current is used. jpayne@68: jpayne@68: proc auto_load {cmd {namespace {}}} { jpayne@68: global auto_index auto_path jpayne@68: jpayne@68: if {$namespace eq ""} { jpayne@68: set namespace [uplevel 1 [list ::namespace current]] jpayne@68: } jpayne@68: set nameList [auto_qualify $cmd $namespace] jpayne@68: # workaround non canonical auto_index entries that might be around jpayne@68: # from older auto_mkindex versions jpayne@68: lappend nameList $cmd jpayne@68: foreach name $nameList { jpayne@68: if {[info exists auto_index($name)]} { jpayne@68: namespace eval :: $auto_index($name) jpayne@68: # There's a couple of ways to look for a command of a given jpayne@68: # name. One is to use jpayne@68: # info commands $name jpayne@68: # Unfortunately, if the name has glob-magic chars in it like * jpayne@68: # or [], it may not match. For our purposes here, a better jpayne@68: # route is to use jpayne@68: # namespace which -command $name jpayne@68: if {[namespace which -command $name] ne ""} { jpayne@68: return 1 jpayne@68: } jpayne@68: } jpayne@68: } jpayne@68: if {![info exists auto_path]} { jpayne@68: return 0 jpayne@68: } jpayne@68: jpayne@68: if {![auto_load_index]} { jpayne@68: return 0 jpayne@68: } jpayne@68: foreach name $nameList { jpayne@68: if {[info exists auto_index($name)]} { jpayne@68: namespace eval :: $auto_index($name) jpayne@68: if {[namespace which -command $name] ne ""} { jpayne@68: return 1 jpayne@68: } jpayne@68: } jpayne@68: } jpayne@68: return 0 jpayne@68: } jpayne@68: jpayne@68: # auto_load_index -- jpayne@68: # Loads the contents of tclIndex files on the auto_path directory jpayne@68: # list. This is usually invoked within auto_load to load the index jpayne@68: # of available commands. Returns 1 if the index is loaded, and 0 if jpayne@68: # the index is already loaded and up to date. jpayne@68: # jpayne@68: # Arguments: jpayne@68: # None. jpayne@68: jpayne@68: proc auto_load_index {} { jpayne@68: variable ::tcl::auto_oldpath jpayne@68: global auto_index auto_path jpayne@68: jpayne@68: if {[info exists auto_oldpath] && ($auto_oldpath eq $auto_path)} { jpayne@68: return 0 jpayne@68: } jpayne@68: set auto_oldpath $auto_path jpayne@68: jpayne@68: # Check if we are a safe interpreter. In that case, we support only jpayne@68: # newer format tclIndex files. jpayne@68: jpayne@68: set issafe [interp issafe] jpayne@68: for {set i [expr {[llength $auto_path] - 1}]} {$i >= 0} {incr i -1} { jpayne@68: set dir [lindex $auto_path $i] jpayne@68: set f "" jpayne@68: if {$issafe} { jpayne@68: catch {source [file join $dir tclIndex]} jpayne@68: } elseif {[catch {set f [open [file join $dir tclIndex]]}]} { jpayne@68: continue jpayne@68: } else { jpayne@68: set error [catch { jpayne@68: fconfigure $f -eofchar "\032 {}" jpayne@68: set id [gets $f] jpayne@68: if {$id eq "# Tcl autoload index file, version 2.0"} { jpayne@68: eval [read $f] jpayne@68: } elseif {$id eq "# Tcl autoload index file: each line identifies a Tcl"} { jpayne@68: while {[gets $f line] >= 0} { jpayne@68: if {([string index $line 0] eq "#") \ jpayne@68: || ([llength $line] != 2)} { jpayne@68: continue jpayne@68: } jpayne@68: set name [lindex $line 0] jpayne@68: set auto_index($name) \ jpayne@68: "source [file join $dir [lindex $line 1]]" jpayne@68: } jpayne@68: } else { jpayne@68: error "[file join $dir tclIndex] isn't a proper Tcl index file" jpayne@68: } jpayne@68: } msg opts] jpayne@68: if {$f ne ""} { jpayne@68: close $f jpayne@68: } jpayne@68: if {$error} { jpayne@68: return -options $opts $msg jpayne@68: } jpayne@68: } jpayne@68: } jpayne@68: return 1 jpayne@68: } jpayne@68: jpayne@68: # auto_qualify -- jpayne@68: # jpayne@68: # Compute a fully qualified names list for use in the auto_index array. jpayne@68: # For historical reasons, commands in the global namespace do not have leading jpayne@68: # :: in the index key. The list has two elements when the command name is jpayne@68: # relative (no leading ::) and the namespace is not the global one. Otherwise jpayne@68: # only one name is returned (and searched in the auto_index). jpayne@68: # jpayne@68: # Arguments - jpayne@68: # cmd The command name. Can be any name accepted for command jpayne@68: # invocations (Like "foo::::bar"). jpayne@68: # namespace The namespace where the command is being used - must be jpayne@68: # a canonical namespace as returned by [namespace current] jpayne@68: # for instance. jpayne@68: jpayne@68: proc auto_qualify {cmd namespace} { jpayne@68: jpayne@68: # count separators and clean them up jpayne@68: # (making sure that foo:::::bar will be treated as foo::bar) jpayne@68: set n [regsub -all {::+} $cmd :: cmd] jpayne@68: jpayne@68: # Ignore namespace if the name starts with :: jpayne@68: # Handle special case of only leading :: jpayne@68: jpayne@68: # Before each return case we give an example of which category it is jpayne@68: # with the following form : jpayne@68: # (inputCmd, inputNameSpace) -> output jpayne@68: jpayne@68: if {[string match ::* $cmd]} { jpayne@68: if {$n > 1} { jpayne@68: # (::foo::bar , *) -> ::foo::bar jpayne@68: return [list $cmd] jpayne@68: } else { jpayne@68: # (::global , *) -> global jpayne@68: return [list [string range $cmd 2 end]] jpayne@68: } jpayne@68: } jpayne@68: jpayne@68: # Potentially returning 2 elements to try : jpayne@68: # (if the current namespace is not the global one) jpayne@68: jpayne@68: if {$n == 0} { jpayne@68: if {$namespace eq "::"} { jpayne@68: # (nocolons , ::) -> nocolons jpayne@68: return [list $cmd] jpayne@68: } else { jpayne@68: # (nocolons , ::sub) -> ::sub::nocolons nocolons jpayne@68: return [list ${namespace}::$cmd $cmd] jpayne@68: } jpayne@68: } elseif {$namespace eq "::"} { jpayne@68: # (foo::bar , ::) -> ::foo::bar jpayne@68: return [list ::$cmd] jpayne@68: } else { jpayne@68: # (foo::bar , ::sub) -> ::sub::foo::bar ::foo::bar jpayne@68: return [list ${namespace}::$cmd ::$cmd] jpayne@68: } jpayne@68: } jpayne@68: jpayne@68: # auto_import -- jpayne@68: # jpayne@68: # Invoked during "namespace import" to make see if the imported commands jpayne@68: # reside in an autoloaded library. If so, the commands are loaded so jpayne@68: # that they will be available for the import links. If not, then this jpayne@68: # procedure does nothing. jpayne@68: # jpayne@68: # Arguments - jpayne@68: # pattern The pattern of commands being imported (like "foo::*") jpayne@68: # a canonical namespace as returned by [namespace current] jpayne@68: jpayne@68: proc auto_import {pattern} { jpayne@68: global auto_index jpayne@68: jpayne@68: # If no namespace is specified, this will be an error case jpayne@68: jpayne@68: if {![string match *::* $pattern]} { jpayne@68: return jpayne@68: } jpayne@68: jpayne@68: set ns [uplevel 1 [list ::namespace current]] jpayne@68: set patternList [auto_qualify $pattern $ns] jpayne@68: jpayne@68: auto_load_index jpayne@68: jpayne@68: foreach pattern $patternList { jpayne@68: foreach name [array names auto_index $pattern] { jpayne@68: if {([namespace which -command $name] eq "") jpayne@68: && ([namespace qualifiers $pattern] eq [namespace qualifiers $name])} { jpayne@68: namespace eval :: $auto_index($name) jpayne@68: } jpayne@68: } jpayne@68: } jpayne@68: } jpayne@68: jpayne@68: # auto_execok -- jpayne@68: # jpayne@68: # Returns string that indicates name of program to execute if jpayne@68: # name corresponds to a shell builtin or an executable in the jpayne@68: # Windows search path, or "" otherwise. Builds an associative jpayne@68: # array auto_execs that caches information about previous checks, jpayne@68: # for speed. jpayne@68: # jpayne@68: # Arguments: jpayne@68: # name - Name of a command. jpayne@68: jpayne@68: if {$tcl_platform(platform) eq "windows"} { jpayne@68: # Windows version. jpayne@68: # jpayne@68: # Note that file executable doesn't work under Windows, so we have to jpayne@68: # look for files with .exe, .com, or .bat extensions. Also, the path jpayne@68: # may be in the Path or PATH environment variables, and path jpayne@68: # components are separated with semicolons, not colons as under Unix. jpayne@68: # jpayne@68: proc auto_execok name { jpayne@68: global auto_execs env tcl_platform jpayne@68: jpayne@68: if {[info exists auto_execs($name)]} { jpayne@68: return $auto_execs($name) jpayne@68: } jpayne@68: set auto_execs($name) "" jpayne@68: jpayne@68: set shellBuiltins [list assoc cls copy date del dir echo erase exit ftype \ jpayne@68: md mkdir mklink move rd ren rename rmdir start time type ver vol] jpayne@68: if {[info exists env(PATHEXT)]} { jpayne@68: # Add an initial ; to have the {} extension check first. jpayne@68: set execExtensions [split ";$env(PATHEXT)" ";"] jpayne@68: } else { jpayne@68: set execExtensions [list {} .com .exe .bat .cmd] jpayne@68: } jpayne@68: jpayne@68: if {[string tolower $name] in $shellBuiltins} { jpayne@68: # When this is command.com for some reason on Win2K, Tcl won't jpayne@68: # exec it unless the case is right, which this corrects. COMSPEC jpayne@68: # may not point to a real file, so do the check. jpayne@68: set cmd $env(COMSPEC) jpayne@68: if {[file exists $cmd]} { jpayne@68: set cmd [file attributes $cmd -shortname] jpayne@68: } jpayne@68: return [set auto_execs($name) [list $cmd /c $name]] jpayne@68: } jpayne@68: jpayne@68: if {[llength [file split $name]] != 1} { jpayne@68: foreach ext $execExtensions { jpayne@68: set file ${name}${ext} jpayne@68: if {[file exists $file] && ![file isdirectory $file]} { jpayne@68: return [set auto_execs($name) [list $file]] jpayne@68: } jpayne@68: } jpayne@68: return "" jpayne@68: } jpayne@68: jpayne@68: set path "[file dirname [info nameof]];.;" jpayne@68: if {[info exists env(SystemRoot)]} { jpayne@68: set windir $env(SystemRoot) jpayne@68: } elseif {[info exists env(WINDIR)]} { jpayne@68: set windir $env(WINDIR) jpayne@68: } jpayne@68: if {[info exists windir]} { jpayne@68: if {$tcl_platform(os) eq "Windows NT"} { jpayne@68: append path "$windir/system32;" jpayne@68: } jpayne@68: append path "$windir/system;$windir;" jpayne@68: } jpayne@68: jpayne@68: foreach var {PATH Path path} { jpayne@68: if {[info exists env($var)]} { jpayne@68: append path ";$env($var)" jpayne@68: } jpayne@68: } jpayne@68: jpayne@68: foreach ext $execExtensions { jpayne@68: unset -nocomplain checked jpayne@68: foreach dir [split $path {;}] { jpayne@68: # Skip already checked directories jpayne@68: if {[info exists checked($dir)] || ($dir eq "")} { jpayne@68: continue jpayne@68: } jpayne@68: set checked($dir) {} jpayne@68: set file [file join $dir ${name}${ext}] jpayne@68: if {[file exists $file] && ![file isdirectory $file]} { jpayne@68: return [set auto_execs($name) [list $file]] jpayne@68: } jpayne@68: } jpayne@68: } jpayne@68: return "" jpayne@68: } jpayne@68: jpayne@68: } else { jpayne@68: # Unix version. jpayne@68: # jpayne@68: proc auto_execok name { jpayne@68: global auto_execs env jpayne@68: jpayne@68: if {[info exists auto_execs($name)]} { jpayne@68: return $auto_execs($name) jpayne@68: } jpayne@68: set auto_execs($name) "" jpayne@68: if {[llength [file split $name]] != 1} { jpayne@68: if {[file executable $name] && ![file isdirectory $name]} { jpayne@68: set auto_execs($name) [list $name] jpayne@68: } jpayne@68: return $auto_execs($name) jpayne@68: } jpayne@68: foreach dir [split $env(PATH) :] { jpayne@68: if {$dir eq ""} { jpayne@68: set dir . jpayne@68: } jpayne@68: set file [file join $dir $name] jpayne@68: if {[file executable $file] && ![file isdirectory $file]} { jpayne@68: set auto_execs($name) [list $file] jpayne@68: return $auto_execs($name) jpayne@68: } jpayne@68: } jpayne@68: return "" jpayne@68: } jpayne@68: jpayne@68: } jpayne@68: jpayne@68: # ::tcl::CopyDirectory -- jpayne@68: # jpayne@68: # This procedure is called by Tcl's core when attempts to call the jpayne@68: # filesystem's copydirectory function fail. The semantics of the call jpayne@68: # are that 'dest' does not yet exist, i.e. dest should become the exact jpayne@68: # image of src. If dest does exist, we throw an error. jpayne@68: # jpayne@68: # Note that making changes to this procedure can change the results jpayne@68: # of running Tcl's tests. jpayne@68: # jpayne@68: # Arguments: jpayne@68: # action - "renaming" or "copying" jpayne@68: # src - source directory jpayne@68: # dest - destination directory jpayne@68: proc tcl::CopyDirectory {action src dest} { jpayne@68: set nsrc [file normalize $src] jpayne@68: set ndest [file normalize $dest] jpayne@68: jpayne@68: if {$action eq "renaming"} { jpayne@68: # Can't rename volumes. We could give a more precise jpayne@68: # error message here, but that would break the test suite. jpayne@68: if {$nsrc in [file volumes]} { jpayne@68: return -code error "error $action \"$src\" to\ jpayne@68: \"$dest\": trying to rename a volume or move a directory\ jpayne@68: into itself" jpayne@68: } jpayne@68: } jpayne@68: if {[file exists $dest]} { jpayne@68: if {$nsrc eq $ndest} { jpayne@68: return -code error "error $action \"$src\" to\ jpayne@68: \"$dest\": trying to rename a volume or move a directory\ jpayne@68: into itself" jpayne@68: } jpayne@68: if {$action eq "copying"} { jpayne@68: # We used to throw an error here, but, looking more closely jpayne@68: # at the core copy code in tclFCmd.c, if the destination jpayne@68: # exists, then we should only call this function if -force jpayne@68: # is true, which means we just want to over-write. So, jpayne@68: # the following code is now commented out. jpayne@68: # jpayne@68: # return -code error "error $action \"$src\" to\ jpayne@68: # \"$dest\": file already exists" jpayne@68: } else { jpayne@68: # Depending on the platform, and on the current jpayne@68: # working directory, the directories '.', '..' jpayne@68: # can be returned in various combinations. Anyway, jpayne@68: # if any other file is returned, we must signal an error. jpayne@68: set existing [glob -nocomplain -directory $dest * .*] jpayne@68: lappend existing {*}[glob -nocomplain -directory $dest \ jpayne@68: -type hidden * .*] jpayne@68: foreach s $existing { jpayne@68: if {[file tail $s] ni {. ..}} { jpayne@68: return -code error "error $action \"$src\" to\ jpayne@68: \"$dest\": file already exists" jpayne@68: } jpayne@68: } jpayne@68: } jpayne@68: } else { jpayne@68: if {[string first $nsrc $ndest] >= 0} { jpayne@68: set srclen [expr {[llength [file split $nsrc]] - 1}] jpayne@68: set ndest [lindex [file split $ndest] $srclen] jpayne@68: if {$ndest eq [file tail $nsrc]} { jpayne@68: return -code error "error $action \"$src\" to\ jpayne@68: \"$dest\": trying to rename a volume or move a directory\ jpayne@68: into itself" jpayne@68: } jpayne@68: } jpayne@68: file mkdir $dest jpayne@68: } jpayne@68: # Have to be careful to capture both visible and hidden files. jpayne@68: # We will also be more generous to the file system and not jpayne@68: # assume the hidden and non-hidden lists are non-overlapping. jpayne@68: # jpayne@68: # On Unix 'hidden' files begin with '.'. On other platforms jpayne@68: # or filesystems hidden files may have other interpretations. jpayne@68: set filelist [concat [glob -nocomplain -directory $src *] \ jpayne@68: [glob -nocomplain -directory $src -types hidden *]] jpayne@68: jpayne@68: foreach s [lsort -unique $filelist] { jpayne@68: if {[file tail $s] ni {. ..}} { jpayne@68: file copy -force -- $s [file join $dest [file tail $s]] jpayne@68: } jpayne@68: } jpayne@68: return jpayne@68: }