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