diff options
Diffstat (limited to 'library/init.tcl')
| -rw-r--r-- | library/init.tcl | 313 |
1 files changed, 187 insertions, 126 deletions
diff --git a/library/init.tcl b/library/init.tcl index 09c3418..62729e6 100644 --- a/library/init.tcl +++ b/library/init.tcl @@ -3,19 +3,20 @@ # Default system startup file for Tcl-based applications. Defines # "unknown" procedure and auto-load facilities. # -# Copyright © 1991-1993 The Regents of the University of California. -# Copyright © 1994-1996 Sun Microsystems, Inc. -# Copyright © 1998-1999 Scriptics Corporation. -# Copyright © 2004 Kevin B. Kenny. -# Copyright © 2018 Sean Woods -# -# All rights reserved. +# Copyright (c) 1991-1993 The Regents of the University of California. +# Copyright (c) 1994-1996 Sun Microsystems, Inc. +# Copyright (c) 1998-1999 Scriptics Corporation. +# Copyright (c) 2004 by Kevin B. Kenny. All rights reserved. # # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # -package require -exact tcl 8.7b1 +# This test intentionally written in pre-7.5 Tcl +if {[info commands package] == ""} { + error "version mismatch: library\nscripts expect Tcl version 7.5b1 or later but the loaded version is\nonly [info patchlevel]" +} +package require -exact Tcl 8.5.18 # Compute the auto path to use in this interpreter. # The values on the path come from several locations: @@ -36,52 +37,121 @@ package require -exact tcl 8.7b1 # tcl_pkgPath, which is set by the platform-specific initialization routines # On UNIX it is compiled in # On Windows, it is not used -# -# (Ticket 41c9857bdd) In a safe interpreter, this file does not set -# ::auto_path (other than to {} if it is undefined). The caller, typically -# a Safe Base command, is responsible for setting ::auto_path. if {![info exists auto_path]} { - if {[info exists env(TCLLIBPATH)] && (![interp issafe])} { + if {[info exists env(TCLLIBPATH)]} { set auto_path $env(TCLLIBPATH) } else { set auto_path "" } } - namespace eval tcl { - if {![interp issafe]} { - variable Dir - foreach Dir [list $::tcl_library [file dirname $::tcl_library]] { - if {$Dir ni $::auto_path} { - lappend ::auto_path $Dir - } - } - set Dir [file join [file dirname [file dirname \ - [info nameofexecutable]]] lib] + variable Dir + foreach Dir [list $::tcl_library [file dirname $::tcl_library]] { if {$Dir ni $::auto_path} { lappend ::auto_path $Dir } - if {[info exists ::tcl_pkgPath]} { catch { - foreach Dir $::tcl_pkgPath { - if {$Dir ni $::auto_path} { - lappend ::auto_path $Dir - } + } + set Dir [file join [file dirname [file dirname \ + [info nameofexecutable]]] lib] + if {$Dir ni $::auto_path} { + lappend ::auto_path $Dir + } + catch { + foreach Dir $::tcl_pkgPath { + if {$Dir ni $::auto_path} { + lappend ::auto_path $Dir } - }} + } + } - variable Path [encoding dirs] - set Dir [file join $::tcl_library encoding] - if {$Dir ni $Path} { + if {![interp issafe]} { + variable Path [encoding dirs] + set Dir [file join $::tcl_library encoding] + if {$Dir ni $Path} { lappend Path $Dir encoding dirs $Path + } + } + + # TIP #255 min and max functions + namespace eval mathfunc { + proc min {args} { + if {![llength $args]} { + return -code error \ + "too few arguments to math function \"min\"" + } + set val Inf + foreach arg $args { + # This will handle forcing the numeric value without + # ruining the internal type of a numeric object + if {[catch {expr {double($arg)}} err]} { + return -code error $err + } + if {$arg < $val} {set val $arg} + } + return $val + } + proc max {args} { + if {![llength $args]} { + return -code error \ + "too few arguments to math function \"max\"" + } + set val -Inf + foreach arg $args { + # This will handle forcing the numeric value without + # ruining the internal type of a numeric object + if {[catch {expr {double($arg)}} err]} { + return -code error $err + } + if {$arg > $val} {set val $arg} + } + return $val } - unset Dir Path + namespace export min max } } -namespace eval tcl::Pkg {} - +# Windows specific end of initialization + +if {(![interp issafe]) && ($tcl_platform(platform) eq "windows")} { + namespace eval tcl { + proc EnvTraceProc {lo n1 n2 op} { + global env + set x $env($n2) + set env($lo) $x + set env([string toupper $lo]) $x + } + proc InitWinEnv {} { + global env tcl_platform + foreach p [array names env] { + set u [string toupper $p] + if {$u ne $p} { + switch -- $u { + COMSPEC - + PATH { + set temp $env($p) + unset env($p) + set env($u) $temp + trace add variable env($p) write \ + [namespace code [list EnvTraceProc $p]] + trace add variable env($u) write \ + [namespace code [list EnvTraceProc $p]] + } + } + } + } + if {![info exists env(COMSPEC)]} { + if {$tcl_platform(os) eq "Windows NT"} { + set env(COMSPEC) cmd.exe + } else { + set env(COMSPEC) command.com + } + } + } + InitWinEnv + } +} # Setup the unknown package handler @@ -103,20 +173,25 @@ if {[interp issafe]} { namespace eval ::tcl::clock [list variable TclLibDir $::tcl_library] - proc ::tcl::initClock {} { + proc clock args { + namespace eval ::tcl::clock [list namespace ensemble create -command \ + [uplevel 1 [list namespace origin [lindex [info level 0] 0]]] \ + -subcommands { + add clicks format microseconds milliseconds scan seconds + }] + # Auto-loading stubs for 'clock.tcl' foreach cmd {add format scan} { proc ::tcl::clock::$cmd args { variable TclLibDir - source [file join $TclLibDir clock.tcl] + source -encoding utf-8 [file join $TclLibDir clock.tcl] return [uplevel 1 [info level 0]] } } - rename ::tcl::initClock {} + return [uplevel 1 [info level 0]] } - ::tcl::initClock } # Conditionalize for presence of exec. @@ -129,7 +204,7 @@ if {[namespace which -command exec] eq ""} { set auto_noexec 1 } -# Define a log command (which can be overwritten to log errors +# Define a log command (which can be overwitten to log errors # differently, specially when stderr is not available) if {[namespace which -command tclLog] eq ""} { @@ -143,9 +218,11 @@ if {[namespace which -command tclLog] eq ""} { # exist in the interpreter. It takes the following steps to make the # command available: # -# 1. See if the autoload facility can locate the command in a +# 1. See if the command has the form "namespace inscope ns cmd" and +# if so, concatenate its arguments onto the end and evaluate it. +# 2. See if the autoload facility can locate the command in a # Tcl script file. If so, load it and execute it. -# 2. If the command was invoked interactively at top-level: +# 3. If the command was invoked interactively at top-level: # (a) see if the command exists as an executable UNIX program. # If so, "exec" the command. # (b) see if the command requests csh-like history substitution @@ -162,14 +239,22 @@ proc unknown args { variable ::tcl::UnknownPending global auto_noexec auto_noload env tcl_interactive errorInfo errorCode - if {[info exists errorInfo]} { - set savedErrorInfo $errorInfo - } - if {[info exists errorCode]} { - set savedErrorCode $errorCode + # If the command word has the form "namespace inscope ns cmd" + # then concatenate its arguments onto the end and evaluate it. + + set cmd [lindex $args 0] + if {[regexp "^:*namespace\[ \t\n\]+inscope" $cmd] && [llength $cmd] == 4} { + #return -code error "You need an {*}" + set arglist [lrange $args 1 end] + set ret [catch {uplevel 1 ::$cmd $arglist} result opts] + dict unset opts -errorinfo + dict incr opts -level + return -options $opts $result } - set name [lindex $args 0] + catch {set savedErrorInfo $errorInfo} + catch {set savedErrorCode $errorCode} + set name $cmd if {![info exists auto_noload]} { # # Make sure we're not trying to load the same proc twice. @@ -211,16 +296,21 @@ proc unknown args { set errInfo [dict get $opts -errorinfo] set errCode [dict get $opts -errorcode] set cinfo $args - if {[string length [encoding convertto utf-8 $cinfo]] > 150} { + if {[string bytelength $cinfo] > 150} { set cinfo [string range $cinfo 0 150] - while {[string length [encoding convertto utf-8 $cinfo]] > 150} { + while {[string bytelength $cinfo] > 150} { set cinfo [string range $cinfo 0 end-1] } append cinfo ... } - set tail "\n (\"uplevel\" body line 1)\n invoked\ - from within\n\"uplevel 1 \$args\"" - set expect "$msg\n while executing\n\"$cinfo\"$tail" + append cinfo "\"\n (\"uplevel\" body line 1)" + append cinfo "\n invoked from within" + append cinfo "\n\"uplevel 1 \$args\"" + # + # Try each possible form of the stack trace + # and trim the extra contribution from the matching case + # + set expect "$msg\n while executing\n\"$cinfo" if {$errInfo eq $expect} { # # The stack has only the eval from the expanded command @@ -234,32 +324,21 @@ proc unknown args { # Stack trace is nested, trim off just the contribution # from the extra "eval" of $args due to the "catch" above. # - set last [string last $tail $errInfo] - if {$last + [string length $tail] != [string length $errInfo]} { - # Very likely cannot happen - return -options $opts $msg - } - set errInfo [string range $errInfo 0 $last-1] - set tail "\"$cinfo\"" - set last [string last $tail $errInfo] - if {$last < 0 || $last + [string length $tail] != [string length $errInfo]} { - return -code error -errorcode $errCode \ - -errorinfo $errInfo $msg - } - set errInfo [string range $errInfo 0 $last-1] - set tail "\n invoked from within\n" - set last [string last $tail $errInfo] - if {$last + [string length $tail] == [string length $errInfo]} { - return -code error -errorcode $errCode \ - -errorinfo [string range $errInfo 0 $last-1] $msg - } - set tail "\n while executing\n" - set last [string last $tail $errInfo] - if {$last + [string length $tail] == [string length $errInfo]} { - return -code error -errorcode $errCode \ - -errorinfo [string range $errInfo 0 $last-1] $msg + set expect "\n invoked from within\n\"$cinfo" + set exlen [string length $expect] + set eilen [string length $errInfo] + set i [expr {$eilen - $exlen - 1}] + set einfo [string range $errInfo 0 $i] + # + # For now verify that $errInfo consists of what we are about + # to return plus what we expected to trim off. + # + if {$errInfo ne "$einfo$expect"} { + error "Tcl bug: unexpected stack trace in \"unknown\"" {} \ + [list CORE UNKNOWN BADTRACE $einfo $expect $errInfo] } - return -options $opts $msg + return -code error -errorcode $errCode \ + -errorinfo $einfo $msg } else { dict incr opts -level return -options $opts $msg @@ -300,14 +379,14 @@ proc unknown args { return -options $::tcl::UnknownOptions $::tcl::UnknownResult } - set ret [catch [list uplevel 1 [list info commands $name*]] candidates] + set ret [catch {set candidates [info commands $name*]} msg] if {$name eq "::"} { set name "" } if {$ret != 0} { dict append opts -errorinfo \ "\n (expanding command prefix \"$name\" in unknown)" - return -options $opts $candidates + return -options $opts $msg } # Filter out bogus matches when $name contained # a glob-special char [Bug 946952] @@ -333,8 +412,7 @@ proc unknown args { return -code error "ambiguous command name \"$name\": [lsort $cmds]" } } - return -code error -errorcode [list TCL LOOKUP COMMAND $name] \ - "invalid command name \"$name\"" + return -code error "invalid command name \"$name\"" } # auto_load -- @@ -352,20 +430,16 @@ proc unknown args { proc auto_load {cmd {namespace {}}} { global auto_index auto_path - # qualify names: if {$namespace eq ""} { set namespace [uplevel 1 [list ::namespace current]] } set nameList [auto_qualify $cmd $namespace] # workaround non canonical auto_index entries that might be around # from older auto_mkindex versions - if {$cmd ni $nameList} {lappend nameList $cmd} - - # try to load (and create sub-cmd handler "_sub_load_cmd" for further usage): - foreach name $nameList [set _sub_load_cmd { - # via auto_index: + lappend nameList $cmd + foreach name $nameList { if {[info exists auto_index($name)]} { - namespace inscope :: $auto_index($name) + namespace eval :: $auto_index($name) # There's a couple of ways to look for a command of a given # name. One is to use # info commands $name @@ -377,36 +451,23 @@ proc auto_load {cmd {namespace {}}} { return 1 } } - }] - - # load auto_index if possible: + } if {![info exists auto_path]} { return 0 } + if {![auto_load_index]} { return 0 } - - # try again (something new could be loaded): - foreach name $nameList $_sub_load_cmd - - return 0 -} - -# ::tcl::Pkg::source -- -# This procedure provides an alternative "source" command, which doesn't -# register the file for the "package files" command. Safe interpreters -# don't have to do anything special. -# -# Arguments: -# filename - -proc ::tcl::Pkg::source {filename} { - if {[interp issafe]} { - uplevel 1 [list ::source $filename] - } else { - uplevel 1 [list ::source -nopkg $filename] + foreach name $nameList { + if {[info exists auto_index($name)]} { + namespace eval :: $auto_index($name) + if {[namespace which -command $name] ne ""} { + return 1 + } + } } + return 0 } # auto_load_index -- @@ -440,7 +501,6 @@ proc auto_load_index {} { continue } else { set error [catch { - fconfigure $f -encoding utf-8 -eofchar "\x1A {}" set id [gets $f] if {$id eq "# Tcl autoload index file, version 2.0"} { eval [read $f] @@ -452,7 +512,7 @@ proc auto_load_index {} { } set name [lindex $line 0] set auto_index($name) \ - "::tcl::Pkg::source [file join $dir [lindex $line 1]]" + "source [file join $dir [lindex $line 1]]" } } else { error "[file join $dir tclIndex] isn't a proper Tcl index file" @@ -556,7 +616,7 @@ proc auto_import {pattern} { foreach name [array names auto_index $pattern] { if {([namespace which -command $name] eq "") && ([namespace qualifiers $pattern] eq [namespace qualifiers $name])} { - namespace inscope :: $auto_index($name) + namespace eval :: $auto_index($name) } } } @@ -576,7 +636,7 @@ proc auto_import {pattern} { if {$tcl_platform(platform) eq "windows"} { # Windows version. # -# Note that file executable doesn't work under Windows, so we have to +# Note that info executable doesn't work under Windows, so we have to # look for files with .exe, .com, or .bat extensions. Also, the path # may be in the Path or PATH environment variables, and path # components are separated with semicolons, not colons as under Unix. @@ -589,8 +649,8 @@ proc auto_execok name { } set auto_execs($name) "" - set shellBuiltins [list assoc cls copy date del dir echo erase exit ftype \ - md mkdir mklink move rd ren rename rmdir start time type ver vol] + set shellBuiltins [list cls copy date del dir echo erase md mkdir \ + mklink rd ren rename rmdir start time type ver vol] if {[info exists env(PATHEXT)]} { # Add an initial ; to have the {} extension check first. set execExtensions [split ";$env(PATHEXT)" ";"] @@ -619,14 +679,15 @@ proc auto_execok name { return "" } - set path "[file dirname [info nameofexecutable]];.;" - if {[info exists env(SystemRoot)]} { - set windir $env(SystemRoot) - } elseif {[info exists env(WINDIR)]} { + set path "[file dirname [info nameof]];.;" + if {[info exists env(WINDIR)]} { set windir $env(WINDIR) } if {[info exists windir]} { - append path "$windir/system32;$windir/system;$windir;" + if {$tcl_platform(os) eq "Windows NT"} { + append path "$windir/system32;" + } + append path "$windir/system;$windir;" } foreach var {PATH Path path} { @@ -724,7 +785,7 @@ proc tcl::CopyDirectory {action src dest} { # the following code is now commented out. # # return -code error "error $action \"$src\" to\ - # \"$dest\": file exists" + # \"$dest\": file already exists" } else { # Depending on the platform, and on the current # working directory, the directories '.', '..' @@ -736,12 +797,12 @@ proc tcl::CopyDirectory {action src dest} { foreach s $existing { if {[file tail $s] ni {. ..}} { return -code error "error $action \"$src\" to\ - \"$dest\": file exists" + \"$dest\": file already exists" } } } } else { - if {[string first $nsrc $ndest] >= 0} { + if {[string first $nsrc $ndest] != -1} { set srclen [expr {[llength [file split $nsrc]] - 1}] set ndest [lindex [file split $ndest] $srclen] if {$ndest eq [file tail $nsrc]} { |
