| 1 | # init.tcl -- | 
|---|
| 2 | # | 
|---|
| 3 | # Default system startup file for Tcl-based applications.  Defines | 
|---|
| 4 | # "unknown" procedure and auto-load facilities. | 
|---|
| 5 | # | 
|---|
| 6 | # RCS: @(#) $Id: init.tcl,v 1.104 2008/03/28 17:31:44 dgp Exp $ | 
|---|
| 7 | # | 
|---|
| 8 | # Copyright (c) 1991-1993 The Regents of the University of California. | 
|---|
| 9 | # Copyright (c) 1994-1996 Sun Microsystems, Inc. | 
|---|
| 10 | # Copyright (c) 1998-1999 Scriptics Corporation. | 
|---|
| 11 | # Copyright (c) 2004 by Kevin B. Kenny.  All rights reserved. | 
|---|
| 12 | # | 
|---|
| 13 | # See the file "license.terms" for information on usage and redistribution | 
|---|
| 14 | # of this file, and for a DISCLAIMER OF ALL WARRANTIES. | 
|---|
| 15 | # | 
|---|
| 16 |  | 
|---|
| 17 | if {[info commands package] == ""} { | 
|---|
| 18 | error "version mismatch: library\nscripts expect Tcl version 7.5b1 or later but the loaded version is\nonly [info patchlevel]" | 
|---|
| 19 | } | 
|---|
| 20 | package require -exact Tcl 8.5.2 | 
|---|
| 21 |  | 
|---|
| 22 | # Compute the auto path to use in this interpreter. | 
|---|
| 23 | # The values on the path come from several locations: | 
|---|
| 24 | # | 
|---|
| 25 | # The environment variable TCLLIBPATH | 
|---|
| 26 | # | 
|---|
| 27 | # tcl_library, which is the directory containing this init.tcl script. | 
|---|
| 28 | # [tclInit] (Tcl_Init()) searches around for the directory containing this | 
|---|
| 29 | # init.tcl and defines tcl_library to that location before sourcing it. | 
|---|
| 30 | # | 
|---|
| 31 | # The parent directory of tcl_library. Adding the parent | 
|---|
| 32 | # means that packages in peer directories will be found automatically. | 
|---|
| 33 | # | 
|---|
| 34 | # Also add the directory ../lib relative to the directory where the | 
|---|
| 35 | # executable is located.  This is meant to find binary packages for the | 
|---|
| 36 | # same architecture as the current executable. | 
|---|
| 37 | # | 
|---|
| 38 | # tcl_pkgPath, which is set by the platform-specific initialization routines | 
|---|
| 39 | #       On UNIX it is compiled in | 
|---|
| 40 | #       On Windows, it is not used | 
|---|
| 41 |  | 
|---|
| 42 | if {![info exists auto_path]} { | 
|---|
| 43 | if {[info exists env(TCLLIBPATH)]} { | 
|---|
| 44 | set auto_path $env(TCLLIBPATH) | 
|---|
| 45 | } else { | 
|---|
| 46 | set auto_path "" | 
|---|
| 47 | } | 
|---|
| 48 | } | 
|---|
| 49 | namespace eval tcl { | 
|---|
| 50 | variable Dir | 
|---|
| 51 | foreach Dir [list $::tcl_library [file dirname $::tcl_library]] { | 
|---|
| 52 | if {$Dir ni $::auto_path} { | 
|---|
| 53 | lappend ::auto_path $Dir | 
|---|
| 54 | } | 
|---|
| 55 | } | 
|---|
| 56 | set Dir [file join [file dirname [file dirname \ | 
|---|
| 57 | [info nameofexecutable]]] lib] | 
|---|
| 58 | if {$Dir ni $::auto_path} { | 
|---|
| 59 | lappend ::auto_path $Dir | 
|---|
| 60 | } | 
|---|
| 61 | catch { | 
|---|
| 62 | foreach Dir $::tcl_pkgPath { | 
|---|
| 63 | if {$Dir ni $::auto_path} { | 
|---|
| 64 | lappend ::auto_path $Dir | 
|---|
| 65 | } | 
|---|
| 66 | } | 
|---|
| 67 | } | 
|---|
| 68 |  | 
|---|
| 69 | if {![interp issafe]} { | 
|---|
| 70 | variable Path [encoding dirs] | 
|---|
| 71 | set Dir [file join $::tcl_library encoding] | 
|---|
| 72 | if {$Dir ni $Path} { | 
|---|
| 73 | lappend Path $Dir | 
|---|
| 74 | encoding dirs $Path | 
|---|
| 75 | } | 
|---|
| 76 | } | 
|---|
| 77 |  | 
|---|
| 78 | # TIP #255 min and max functions | 
|---|
| 79 | namespace eval mathfunc { | 
|---|
| 80 | proc min {args} { | 
|---|
| 81 | if {[llength $args] == 0} { | 
|---|
| 82 | return -code error \ | 
|---|
| 83 | "too few arguments to math function \"min\"" | 
|---|
| 84 | } | 
|---|
| 85 | set val Inf | 
|---|
| 86 | foreach arg $args { | 
|---|
| 87 | # This will handle forcing the numeric value without | 
|---|
| 88 | # ruining the internal type of a numeric object | 
|---|
| 89 | if {[catch {expr {double($arg)}} err]} { | 
|---|
| 90 | return -code error $err | 
|---|
| 91 | } | 
|---|
| 92 | if {$arg < $val} { set val $arg } | 
|---|
| 93 | } | 
|---|
| 94 | return $val | 
|---|
| 95 | } | 
|---|
| 96 | proc max {args} { | 
|---|
| 97 | if {[llength $args] == 0} { | 
|---|
| 98 | return -code error \ | 
|---|
| 99 | "too few arguments to math function \"max\"" | 
|---|
| 100 | } | 
|---|
| 101 | set val -Inf | 
|---|
| 102 | foreach arg $args { | 
|---|
| 103 | # This will handle forcing the numeric value without | 
|---|
| 104 | # ruining the internal type of a numeric object | 
|---|
| 105 | if {[catch {expr {double($arg)}} err]} { | 
|---|
| 106 | return -code error $err | 
|---|
| 107 | } | 
|---|
| 108 | if {$arg > $val} { set val $arg } | 
|---|
| 109 | } | 
|---|
| 110 | return $val | 
|---|
| 111 | } | 
|---|
| 112 | } | 
|---|
| 113 | } | 
|---|
| 114 |  | 
|---|
| 115 | # Windows specific end of initialization | 
|---|
| 116 |  | 
|---|
| 117 | if {(![interp issafe]) && ($tcl_platform(platform) eq "windows")} { | 
|---|
| 118 | namespace eval tcl { | 
|---|
| 119 | proc EnvTraceProc {lo n1 n2 op} { | 
|---|
| 120 | set x $::env($n2) | 
|---|
| 121 | set ::env($lo) $x | 
|---|
| 122 | set ::env([string toupper $lo]) $x | 
|---|
| 123 | } | 
|---|
| 124 | proc InitWinEnv {} { | 
|---|
| 125 | global env tcl_platform | 
|---|
| 126 | foreach p [array names env] { | 
|---|
| 127 | set u [string toupper $p] | 
|---|
| 128 | if {$u ne $p} { | 
|---|
| 129 | switch -- $u { | 
|---|
| 130 | COMSPEC - | 
|---|
| 131 | PATH { | 
|---|
| 132 | if {![info exists env($u)]} { | 
|---|
| 133 | set env($u) $env($p) | 
|---|
| 134 | } | 
|---|
| 135 | trace add variable env($p) write \ | 
|---|
| 136 | [namespace code [list EnvTraceProc $p]] | 
|---|
| 137 | trace add variable env($u) write \ | 
|---|
| 138 | [namespace code [list EnvTraceProc $p]] | 
|---|
| 139 | } | 
|---|
| 140 | } | 
|---|
| 141 | } | 
|---|
| 142 | } | 
|---|
| 143 | if {![info exists env(COMSPEC)]} { | 
|---|
| 144 | if {$tcl_platform(os) eq "Windows NT"} { | 
|---|
| 145 | set env(COMSPEC) cmd.exe | 
|---|
| 146 | } else { | 
|---|
| 147 | set env(COMSPEC) command.com | 
|---|
| 148 | } | 
|---|
| 149 | } | 
|---|
| 150 | } | 
|---|
| 151 | InitWinEnv | 
|---|
| 152 | } | 
|---|
| 153 | } | 
|---|
| 154 |  | 
|---|
| 155 | # Setup the unknown package handler | 
|---|
| 156 |  | 
|---|
| 157 |  | 
|---|
| 158 | if {[interp issafe]} { | 
|---|
| 159 | package unknown ::tclPkgUnknown | 
|---|
| 160 | } else { | 
|---|
| 161 | # Set up search for Tcl Modules (TIP #189). | 
|---|
| 162 | # and setup platform specific unknown package handlers | 
|---|
| 163 | if {$::tcl_platform(os) eq "Darwin" | 
|---|
| 164 | && $::tcl_platform(platform) eq "unix"} { | 
|---|
| 165 | package unknown {::tcl::tm::UnknownHandler \ | 
|---|
| 166 | {::tcl::MacOSXPkgUnknown ::tclPkgUnknown}} | 
|---|
| 167 | } else { | 
|---|
| 168 | package unknown {::tcl::tm::UnknownHandler ::tclPkgUnknown} | 
|---|
| 169 | } | 
|---|
| 170 |  | 
|---|
| 171 | # Set up the 'clock' ensemble | 
|---|
| 172 |  | 
|---|
| 173 | namespace eval ::tcl::clock [list variable TclLibDir $::tcl_library] | 
|---|
| 174 |  | 
|---|
| 175 | proc clock args { | 
|---|
| 176 | namespace eval ::tcl::clock [list namespace ensemble create -command \ | 
|---|
| 177 | [uplevel 1 [list namespace origin [lindex [info level 0] 0]]] \ | 
|---|
| 178 | -subcommands { | 
|---|
| 179 | add clicks format microseconds milliseconds scan seconds | 
|---|
| 180 | }] | 
|---|
| 181 |  | 
|---|
| 182 | # Auto-loading stubs for 'clock.tcl' | 
|---|
| 183 |  | 
|---|
| 184 | foreach cmd {add format scan} { | 
|---|
| 185 | proc ::tcl::clock::$cmd args { | 
|---|
| 186 | variable TclLibDir | 
|---|
| 187 | source -encoding utf-8 [file join $TclLibDir clock.tcl] | 
|---|
| 188 | return [uplevel 1 [info level 0]] | 
|---|
| 189 | } | 
|---|
| 190 | } | 
|---|
| 191 |  | 
|---|
| 192 | return [uplevel 1 [info level 0]] | 
|---|
| 193 | } | 
|---|
| 194 | } | 
|---|
| 195 |  | 
|---|
| 196 | # Conditionalize for presence of exec. | 
|---|
| 197 |  | 
|---|
| 198 | if {[namespace which -command exec] eq ""} { | 
|---|
| 199 |  | 
|---|
| 200 | # Some machines do not have exec. Also, on all | 
|---|
| 201 | # platforms, safe interpreters do not have exec. | 
|---|
| 202 |  | 
|---|
| 203 | set auto_noexec 1 | 
|---|
| 204 | } | 
|---|
| 205 |  | 
|---|
| 206 | # Define a log command (which can be overwitten to log errors | 
|---|
| 207 | # differently, specially when stderr is not available) | 
|---|
| 208 |  | 
|---|
| 209 | if {[namespace which -command tclLog] eq ""} { | 
|---|
| 210 | proc tclLog {string} { | 
|---|
| 211 | catch {puts stderr $string} | 
|---|
| 212 | } | 
|---|
| 213 | } | 
|---|
| 214 |  | 
|---|
| 215 | # unknown -- | 
|---|
| 216 | # This procedure is called when a Tcl command is invoked that doesn't | 
|---|
| 217 | # exist in the interpreter.  It takes the following steps to make the | 
|---|
| 218 | # command available: | 
|---|
| 219 | # | 
|---|
| 220 | #       1. See if the command has the form "namespace inscope ns cmd" and | 
|---|
| 221 | #          if so, concatenate its arguments onto the end and evaluate it. | 
|---|
| 222 | #       2. See if the autoload facility can locate the command in a | 
|---|
| 223 | #          Tcl script file.  If so, load it and execute it. | 
|---|
| 224 | #       3. If the command was invoked interactively at top-level: | 
|---|
| 225 | #           (a) see if the command exists as an executable UNIX program. | 
|---|
| 226 | #               If so, "exec" the command. | 
|---|
| 227 | #           (b) see if the command requests csh-like history substitution | 
|---|
| 228 | #               in one of the common forms !!, !<number>, or ^old^new.  If | 
|---|
| 229 | #               so, emulate csh's history substitution. | 
|---|
| 230 | #           (c) see if the command is a unique abbreviation for another | 
|---|
| 231 | #               command.  If so, invoke the command. | 
|---|
| 232 | # | 
|---|
| 233 | # Arguments: | 
|---|
| 234 | # args -        A list whose elements are the words of the original | 
|---|
| 235 | #               command, including the command name. | 
|---|
| 236 |  | 
|---|
| 237 | proc unknown args { | 
|---|
| 238 | variable ::tcl::UnknownPending | 
|---|
| 239 | global auto_noexec auto_noload env tcl_interactive | 
|---|
| 240 |  | 
|---|
| 241 | # If the command word has the form "namespace inscope ns cmd" | 
|---|
| 242 | # then concatenate its arguments onto the end and evaluate it. | 
|---|
| 243 |  | 
|---|
| 244 | set cmd [lindex $args 0] | 
|---|
| 245 | if {[regexp "^:*namespace\[ \t\n\]+inscope" $cmd] && [llength $cmd] == 4} { | 
|---|
| 246 | #return -code error "You need an {*}" | 
|---|
| 247 | set arglist [lrange $args 1 end] | 
|---|
| 248 | set ret [catch {uplevel 1 ::$cmd $arglist} result opts] | 
|---|
| 249 | dict unset opts -errorinfo | 
|---|
| 250 | dict incr opts -level | 
|---|
| 251 | return -options $opts $result | 
|---|
| 252 | } | 
|---|
| 253 |  | 
|---|
| 254 | catch {set savedErrorInfo $::errorInfo} | 
|---|
| 255 | catch {set savedErrorCode $::errorCode} | 
|---|
| 256 | set name $cmd | 
|---|
| 257 | if {![info exists auto_noload]} { | 
|---|
| 258 | # | 
|---|
| 259 | # Make sure we're not trying to load the same proc twice. | 
|---|
| 260 | # | 
|---|
| 261 | if {[info exists UnknownPending($name)]} { | 
|---|
| 262 | return -code error "self-referential recursion\ | 
|---|
| 263 | in \"unknown\" for command \"$name\""; | 
|---|
| 264 | } | 
|---|
| 265 | set UnknownPending($name) pending; | 
|---|
| 266 | set ret [catch { | 
|---|
| 267 | auto_load $name [uplevel 1 {::namespace current}] | 
|---|
| 268 | } msg opts] | 
|---|
| 269 | unset UnknownPending($name); | 
|---|
| 270 | if {$ret != 0} { | 
|---|
| 271 | dict append opts -errorinfo "\n    (autoloading \"$name\")" | 
|---|
| 272 | return -options $opts $msg | 
|---|
| 273 | } | 
|---|
| 274 | if {![array size UnknownPending]} { | 
|---|
| 275 | unset UnknownPending | 
|---|
| 276 | } | 
|---|
| 277 | if {$msg} { | 
|---|
| 278 | catch {set ::errorCode $savedErrorCode} | 
|---|
| 279 | catch {set ::errorInfo $savedErrorInfo} | 
|---|
| 280 | set code [catch {uplevel 1 $args} msg opts] | 
|---|
| 281 | if {$code ==  1} { | 
|---|
| 282 | # | 
|---|
| 283 | # Compute stack trace contribution from the [uplevel]. | 
|---|
| 284 | # Note the dependence on how Tcl_AddErrorInfo, etc. | 
|---|
| 285 | # construct the stack trace. | 
|---|
| 286 | # | 
|---|
| 287 | set errorInfo [dict get $opts -errorinfo] | 
|---|
| 288 | set errorCode [dict get $opts -errorcode] | 
|---|
| 289 | set cinfo $args | 
|---|
| 290 | if {[string bytelength $cinfo] > 150} { | 
|---|
| 291 | set cinfo [string range $cinfo 0 150] | 
|---|
| 292 | while {[string bytelength $cinfo] > 150} { | 
|---|
| 293 | set cinfo [string range $cinfo 0 end-1] | 
|---|
| 294 | } | 
|---|
| 295 | append cinfo ... | 
|---|
| 296 | } | 
|---|
| 297 | append cinfo "\"\n    (\"uplevel\" body line 1)" | 
|---|
| 298 | append cinfo "\n    invoked from within" | 
|---|
| 299 | append cinfo "\n\"uplevel 1 \$args\"" | 
|---|
| 300 | # | 
|---|
| 301 | # Try each possible form of the stack trace | 
|---|
| 302 | # and trim the extra contribution from the matching case | 
|---|
| 303 | # | 
|---|
| 304 | set expect "$msg\n    while executing\n\"$cinfo" | 
|---|
| 305 | if {$errorInfo eq $expect} { | 
|---|
| 306 | # | 
|---|
| 307 | # The stack has only the eval from the expanded command | 
|---|
| 308 | # Do not generate any stack trace here. | 
|---|
| 309 | # | 
|---|
| 310 | dict unset opts -errorinfo | 
|---|
| 311 | dict incr opts -level | 
|---|
| 312 | return -options $opts $msg | 
|---|
| 313 | } | 
|---|
| 314 | # | 
|---|
| 315 | # Stack trace is nested, trim off just the contribution | 
|---|
| 316 | # from the extra "eval" of $args due to the "catch" above. | 
|---|
| 317 | # | 
|---|
| 318 | set expect "\n    invoked from within\n\"$cinfo" | 
|---|
| 319 | set exlen [string length $expect] | 
|---|
| 320 | set eilen [string length $errorInfo] | 
|---|
| 321 | set i [expr {$eilen - $exlen - 1}] | 
|---|
| 322 | set einfo [string range $errorInfo 0 $i] | 
|---|
| 323 | # | 
|---|
| 324 | # For now verify that $errorInfo consists of what we are about | 
|---|
| 325 | # to return plus what we expected to trim off. | 
|---|
| 326 | # | 
|---|
| 327 | if {$errorInfo ne "$einfo$expect"} { | 
|---|
| 328 | error "Tcl bug: unexpected stack trace in \"unknown\"" {} \ | 
|---|
| 329 | [list CORE UNKNOWN BADTRACE $einfo $expect $errorInfo] | 
|---|
| 330 | } | 
|---|
| 331 | return -code error -errorcode $errorCode \ | 
|---|
| 332 | -errorinfo $einfo $msg | 
|---|
| 333 | } else { | 
|---|
| 334 | dict incr opts -level | 
|---|
| 335 | return -options $opts $msg | 
|---|
| 336 | } | 
|---|
| 337 | } | 
|---|
| 338 | } | 
|---|
| 339 |  | 
|---|
| 340 | if {([info level] == 1) && ([info script] eq "") \ | 
|---|
| 341 | && [info exists tcl_interactive] && $tcl_interactive} { | 
|---|
| 342 | if {![info exists auto_noexec]} { | 
|---|
| 343 | set new [auto_execok $name] | 
|---|
| 344 | if {$new ne ""} { | 
|---|
| 345 | set redir "" | 
|---|
| 346 | if {[namespace which -command console] eq ""} { | 
|---|
| 347 | set redir ">&@stdout <@stdin" | 
|---|
| 348 | } | 
|---|
| 349 | uplevel 1 [list ::catch \ | 
|---|
| 350 | [concat exec $redir $new [lrange $args 1 end]] \ | 
|---|
| 351 | ::tcl::UnknownResult ::tcl::UnknownOptions] | 
|---|
| 352 | dict incr ::tcl::UnknownOptions -level | 
|---|
| 353 | return -options $::tcl::UnknownOptions $::tcl::UnknownResult | 
|---|
| 354 | } | 
|---|
| 355 | } | 
|---|
| 356 | if {$name eq "!!"} { | 
|---|
| 357 | set newcmd [history event] | 
|---|
| 358 | } elseif {[regexp {^!(.+)$} $name -> event]} { | 
|---|
| 359 | set newcmd [history event $event] | 
|---|
| 360 | } elseif {[regexp {^\^([^^]*)\^([^^]*)\^?$} $name -> old new]} { | 
|---|
| 361 | set newcmd [history event -1] | 
|---|
| 362 | catch {regsub -all -- $old $newcmd $new newcmd} | 
|---|
| 363 | } | 
|---|
| 364 | if {[info exists newcmd]} { | 
|---|
| 365 | tclLog $newcmd | 
|---|
| 366 | history change $newcmd 0 | 
|---|
| 367 | uplevel 1 [list ::catch $newcmd \ | 
|---|
| 368 | ::tcl::UnknownResult ::tcl::UnknownOptions] | 
|---|
| 369 | dict incr ::tcl::UnknownOptions -level | 
|---|
| 370 | return -options $::tcl::UnknownOptions $::tcl::UnknownResult | 
|---|
| 371 | } | 
|---|
| 372 |  | 
|---|
| 373 | set ret [catch {set candidates [info commands $name*]} msg] | 
|---|
| 374 | if {$name eq "::"} { | 
|---|
| 375 | set name "" | 
|---|
| 376 | } | 
|---|
| 377 | if {$ret != 0} { | 
|---|
| 378 | dict append opts -errorinfo \ | 
|---|
| 379 | "\n    (expanding command prefix \"$name\" in unknown)" | 
|---|
| 380 | return -options $opts $msg | 
|---|
| 381 | } | 
|---|
| 382 | # Filter out bogus matches when $name contained | 
|---|
| 383 | # a glob-special char [Bug 946952] | 
|---|
| 384 | if {$name eq ""} { | 
|---|
| 385 | # Handle empty $name separately due to strangeness | 
|---|
| 386 | # in [string first] (See RFE 1243354) | 
|---|
| 387 | set cmds $candidates | 
|---|
| 388 | } else { | 
|---|
| 389 | set cmds [list] | 
|---|
| 390 | foreach x $candidates { | 
|---|
| 391 | if {[string first $name $x] == 0} { | 
|---|
| 392 | lappend cmds $x | 
|---|
| 393 | } | 
|---|
| 394 | } | 
|---|
| 395 | } | 
|---|
| 396 | if {[llength $cmds] == 1} { | 
|---|
| 397 | uplevel 1 [list ::catch [lreplace $args 0 0 [lindex $cmds 0]] \ | 
|---|
| 398 | ::tcl::UnknownResult ::tcl::UnknownOptions] | 
|---|
| 399 | dict incr ::tcl::UnknownOptions -level | 
|---|
| 400 | return -options $::tcl::UnknownOptions $::tcl::UnknownResult | 
|---|
| 401 | } | 
|---|
| 402 | if {[llength $cmds]} { | 
|---|
| 403 | return -code error "ambiguous command name \"$name\": [lsort $cmds]" | 
|---|
| 404 | } | 
|---|
| 405 | } | 
|---|
| 406 | return -code error "invalid command name \"$name\"" | 
|---|
| 407 | } | 
|---|
| 408 |  | 
|---|
| 409 | # auto_load -- | 
|---|
| 410 | # Checks a collection of library directories to see if a procedure | 
|---|
| 411 | # is defined in one of them.  If so, it sources the appropriate | 
|---|
| 412 | # library file to create the procedure.  Returns 1 if it successfully | 
|---|
| 413 | # loaded the procedure, 0 otherwise. | 
|---|
| 414 | # | 
|---|
| 415 | # Arguments: | 
|---|
| 416 | # cmd -                 Name of the command to find and load. | 
|---|
| 417 | # namespace (optional)  The namespace where the command is being used - must be | 
|---|
| 418 | #                       a canonical namespace as returned [namespace current] | 
|---|
| 419 | #                       for instance. If not given, namespace current is used. | 
|---|
| 420 |  | 
|---|
| 421 | proc auto_load {cmd {namespace {}}} { | 
|---|
| 422 | global auto_index auto_path | 
|---|
| 423 |  | 
|---|
| 424 | if {$namespace eq ""} { | 
|---|
| 425 | set namespace [uplevel 1 [list ::namespace current]] | 
|---|
| 426 | } | 
|---|
| 427 | set nameList [auto_qualify $cmd $namespace] | 
|---|
| 428 | # workaround non canonical auto_index entries that might be around | 
|---|
| 429 | # from older auto_mkindex versions | 
|---|
| 430 | lappend nameList $cmd | 
|---|
| 431 | foreach name $nameList { | 
|---|
| 432 | if {[info exists auto_index($name)]} { | 
|---|
| 433 | namespace eval :: $auto_index($name) | 
|---|
| 434 | # There's a couple of ways to look for a command of a given | 
|---|
| 435 | # name.  One is to use | 
|---|
| 436 | #    info commands $name | 
|---|
| 437 | # Unfortunately, if the name has glob-magic chars in it like * | 
|---|
| 438 | # or [], it may not match.  For our purposes here, a better | 
|---|
| 439 | # route is to use | 
|---|
| 440 | #    namespace which -command $name | 
|---|
| 441 | if {[namespace which -command $name] ne ""} { | 
|---|
| 442 | return 1 | 
|---|
| 443 | } | 
|---|
| 444 | } | 
|---|
| 445 | } | 
|---|
| 446 | if {![info exists auto_path]} { | 
|---|
| 447 | return 0 | 
|---|
| 448 | } | 
|---|
| 449 |  | 
|---|
| 450 | if {![auto_load_index]} { | 
|---|
| 451 | return 0 | 
|---|
| 452 | } | 
|---|
| 453 | foreach name $nameList { | 
|---|
| 454 | if {[info exists auto_index($name)]} { | 
|---|
| 455 | namespace eval :: $auto_index($name) | 
|---|
| 456 | if {[namespace which -command $name] ne ""} { | 
|---|
| 457 | return 1 | 
|---|
| 458 | } | 
|---|
| 459 | } | 
|---|
| 460 | } | 
|---|
| 461 | return 0 | 
|---|
| 462 | } | 
|---|
| 463 |  | 
|---|
| 464 | # auto_load_index -- | 
|---|
| 465 | # Loads the contents of tclIndex files on the auto_path directory | 
|---|
| 466 | # list.  This is usually invoked within auto_load to load the index | 
|---|
| 467 | # of available commands.  Returns 1 if the index is loaded, and 0 if | 
|---|
| 468 | # the index is already loaded and up to date. | 
|---|
| 469 | # | 
|---|
| 470 | # Arguments: | 
|---|
| 471 | # None. | 
|---|
| 472 |  | 
|---|
| 473 | proc auto_load_index {} { | 
|---|
| 474 | variable ::tcl::auto_oldpath | 
|---|
| 475 | global auto_index auto_path | 
|---|
| 476 |  | 
|---|
| 477 | if {[info exists auto_oldpath] && ($auto_oldpath eq $auto_path)} { | 
|---|
| 478 | return 0 | 
|---|
| 479 | } | 
|---|
| 480 | set auto_oldpath $auto_path | 
|---|
| 481 |  | 
|---|
| 482 | # Check if we are a safe interpreter. In that case, we support only | 
|---|
| 483 | # newer format tclIndex files. | 
|---|
| 484 |  | 
|---|
| 485 | set issafe [interp issafe] | 
|---|
| 486 | for {set i [expr {[llength $auto_path] - 1}]} {$i >= 0} {incr i -1} { | 
|---|
| 487 | set dir [lindex $auto_path $i] | 
|---|
| 488 | set f "" | 
|---|
| 489 | if {$issafe} { | 
|---|
| 490 | catch {source [file join $dir tclIndex]} | 
|---|
| 491 | } elseif {[catch {set f [open [file join $dir tclIndex]]}]} { | 
|---|
| 492 | continue | 
|---|
| 493 | } else { | 
|---|
| 494 | set error [catch { | 
|---|
| 495 | set id [gets $f] | 
|---|
| 496 | if {$id eq "# Tcl autoload index file, version 2.0"} { | 
|---|
| 497 | eval [read $f] | 
|---|
| 498 | } elseif {$id eq "# Tcl autoload index file: each line identifies a Tcl"} { | 
|---|
| 499 | while {[gets $f line] >= 0} { | 
|---|
| 500 | if {([string index $line 0] eq "#") \ | 
|---|
| 501 | || ([llength $line] != 2)} { | 
|---|
| 502 | continue | 
|---|
| 503 | } | 
|---|
| 504 | set name [lindex $line 0] | 
|---|
| 505 | set auto_index($name) \ | 
|---|
| 506 | "source [file join $dir [lindex $line 1]]" | 
|---|
| 507 | } | 
|---|
| 508 | } else { | 
|---|
| 509 | error "[file join $dir tclIndex] isn't a proper Tcl index file" | 
|---|
| 510 | } | 
|---|
| 511 | } msg opts] | 
|---|
| 512 | if {$f ne ""} { | 
|---|
| 513 | close $f | 
|---|
| 514 | } | 
|---|
| 515 | if {$error} { | 
|---|
| 516 | return -options $opts $msg | 
|---|
| 517 | } | 
|---|
| 518 | } | 
|---|
| 519 | } | 
|---|
| 520 | return 1 | 
|---|
| 521 | } | 
|---|
| 522 |  | 
|---|
| 523 | # auto_qualify -- | 
|---|
| 524 | # | 
|---|
| 525 | # Compute a fully qualified names list for use in the auto_index array. | 
|---|
| 526 | # For historical reasons, commands in the global namespace do not have leading | 
|---|
| 527 | # :: in the index key. The list has two elements when the command name is | 
|---|
| 528 | # relative (no leading ::) and the namespace is not the global one. Otherwise | 
|---|
| 529 | # only one name is returned (and searched in the auto_index). | 
|---|
| 530 | # | 
|---|
| 531 | # Arguments - | 
|---|
| 532 | # cmd           The command name. Can be any name accepted for command | 
|---|
| 533 | #               invocations (Like "foo::::bar"). | 
|---|
| 534 | # namespace     The namespace where the command is being used - must be | 
|---|
| 535 | #               a canonical namespace as returned by [namespace current] | 
|---|
| 536 | #               for instance. | 
|---|
| 537 |  | 
|---|
| 538 | proc auto_qualify {cmd namespace} { | 
|---|
| 539 |  | 
|---|
| 540 | # count separators and clean them up | 
|---|
| 541 | # (making sure that foo:::::bar will be treated as foo::bar) | 
|---|
| 542 | set n [regsub -all {::+} $cmd :: cmd] | 
|---|
| 543 |  | 
|---|
| 544 | # Ignore namespace if the name starts with :: | 
|---|
| 545 | # Handle special case of only leading :: | 
|---|
| 546 |  | 
|---|
| 547 | # Before each return case we give an example of which category it is | 
|---|
| 548 | # with the following form : | 
|---|
| 549 | # ( inputCmd, inputNameSpace) -> output | 
|---|
| 550 |  | 
|---|
| 551 | if {[string match ::* $cmd]} { | 
|---|
| 552 | if {$n > 1} { | 
|---|
| 553 | # ( ::foo::bar , * ) -> ::foo::bar | 
|---|
| 554 | return [list $cmd] | 
|---|
| 555 | } else { | 
|---|
| 556 | # ( ::global , * ) -> global | 
|---|
| 557 | return [list [string range $cmd 2 end]] | 
|---|
| 558 | } | 
|---|
| 559 | } | 
|---|
| 560 |  | 
|---|
| 561 | # Potentially returning 2 elements to try  : | 
|---|
| 562 | # (if the current namespace is not the global one) | 
|---|
| 563 |  | 
|---|
| 564 | if {$n == 0} { | 
|---|
| 565 | if {$namespace eq "::"} { | 
|---|
| 566 | # ( nocolons , :: ) -> nocolons | 
|---|
| 567 | return [list $cmd] | 
|---|
| 568 | } else { | 
|---|
| 569 | # ( nocolons , ::sub ) -> ::sub::nocolons nocolons | 
|---|
| 570 | return [list ${namespace}::$cmd $cmd] | 
|---|
| 571 | } | 
|---|
| 572 | } elseif {$namespace eq "::"} { | 
|---|
| 573 | #  ( foo::bar , :: ) -> ::foo::bar | 
|---|
| 574 | return [list ::$cmd] | 
|---|
| 575 | } else { | 
|---|
| 576 | # ( foo::bar , ::sub ) -> ::sub::foo::bar ::foo::bar | 
|---|
| 577 | return [list ${namespace}::$cmd ::$cmd] | 
|---|
| 578 | } | 
|---|
| 579 | } | 
|---|
| 580 |  | 
|---|
| 581 | # auto_import -- | 
|---|
| 582 | # | 
|---|
| 583 | # Invoked during "namespace import" to make see if the imported commands | 
|---|
| 584 | # reside in an autoloaded library.  If so, the commands are loaded so | 
|---|
| 585 | # that they will be available for the import links.  If not, then this | 
|---|
| 586 | # procedure does nothing. | 
|---|
| 587 | # | 
|---|
| 588 | # Arguments - | 
|---|
| 589 | # pattern       The pattern of commands being imported (like "foo::*") | 
|---|
| 590 | #               a canonical namespace as returned by [namespace current] | 
|---|
| 591 |  | 
|---|
| 592 | proc auto_import {pattern} { | 
|---|
| 593 | global auto_index | 
|---|
| 594 |  | 
|---|
| 595 | # If no namespace is specified, this will be an error case | 
|---|
| 596 |  | 
|---|
| 597 | if {![string match *::* $pattern]} { | 
|---|
| 598 | return | 
|---|
| 599 | } | 
|---|
| 600 |  | 
|---|
| 601 | set ns [uplevel 1 [list ::namespace current]] | 
|---|
| 602 | set patternList [auto_qualify $pattern $ns] | 
|---|
| 603 |  | 
|---|
| 604 | auto_load_index | 
|---|
| 605 |  | 
|---|
| 606 | foreach pattern $patternList { | 
|---|
| 607 | foreach name [array names auto_index $pattern] { | 
|---|
| 608 | if {([namespace which -command $name] eq "") | 
|---|
| 609 | && ([namespace qualifiers $pattern] eq [namespace qualifiers $name])} { | 
|---|
| 610 | namespace eval :: $auto_index($name) | 
|---|
| 611 | } | 
|---|
| 612 | } | 
|---|
| 613 | } | 
|---|
| 614 | } | 
|---|
| 615 |  | 
|---|
| 616 | # auto_execok -- | 
|---|
| 617 | # | 
|---|
| 618 | # Returns string that indicates name of program to execute if | 
|---|
| 619 | # name corresponds to a shell builtin or an executable in the | 
|---|
| 620 | # Windows search path, or "" otherwise.  Builds an associative | 
|---|
| 621 | # array auto_execs that caches information about previous checks, | 
|---|
| 622 | # for speed. | 
|---|
| 623 | # | 
|---|
| 624 | # Arguments: | 
|---|
| 625 | # name -                        Name of a command. | 
|---|
| 626 |  | 
|---|
| 627 | if {$tcl_platform(platform) eq "windows"} { | 
|---|
| 628 | # Windows version. | 
|---|
| 629 | # | 
|---|
| 630 | # Note that info executable doesn't work under Windows, so we have to | 
|---|
| 631 | # look for files with .exe, .com, or .bat extensions.  Also, the path | 
|---|
| 632 | # may be in the Path or PATH environment variables, and path | 
|---|
| 633 | # components are separated with semicolons, not colons as under Unix. | 
|---|
| 634 | # | 
|---|
| 635 | proc auto_execok name { | 
|---|
| 636 | global auto_execs env tcl_platform | 
|---|
| 637 |  | 
|---|
| 638 | if {[info exists auto_execs($name)]} { | 
|---|
| 639 | return $auto_execs($name) | 
|---|
| 640 | } | 
|---|
| 641 | set auto_execs($name) "" | 
|---|
| 642 |  | 
|---|
| 643 | set shellBuiltins [list cls copy date del erase dir echo mkdir \ | 
|---|
| 644 | md rename ren rmdir rd time type ver vol] | 
|---|
| 645 | if {$tcl_platform(os) eq "Windows NT"} { | 
|---|
| 646 | # NT includes the 'start' built-in | 
|---|
| 647 | lappend shellBuiltins "start" | 
|---|
| 648 | } | 
|---|
| 649 | if {[info exists env(PATHEXT)]} { | 
|---|
| 650 | # Add an initial ; to have the {} extension check first. | 
|---|
| 651 | set execExtensions [split ";$env(PATHEXT)" ";"] | 
|---|
| 652 | } else { | 
|---|
| 653 | set execExtensions [list {} .com .exe .bat] | 
|---|
| 654 | } | 
|---|
| 655 |  | 
|---|
| 656 | if {$name in $shellBuiltins} { | 
|---|
| 657 | # When this is command.com for some reason on Win2K, Tcl won't | 
|---|
| 658 | # exec it unless the case is right, which this corrects.  COMSPEC | 
|---|
| 659 | # may not point to a real file, so do the check. | 
|---|
| 660 | set cmd $env(COMSPEC) | 
|---|
| 661 | if {[file exists $cmd]} { | 
|---|
| 662 | set cmd [file attributes $cmd -shortname] | 
|---|
| 663 | } | 
|---|
| 664 | return [set auto_execs($name) [list $cmd /c $name]] | 
|---|
| 665 | } | 
|---|
| 666 |  | 
|---|
| 667 | if {[llength [file split $name]] != 1} { | 
|---|
| 668 | foreach ext $execExtensions { | 
|---|
| 669 | set file ${name}${ext} | 
|---|
| 670 | if {[file exists $file] && ![file isdirectory $file]} { | 
|---|
| 671 | return [set auto_execs($name) [list $file]] | 
|---|
| 672 | } | 
|---|
| 673 | } | 
|---|
| 674 | return "" | 
|---|
| 675 | } | 
|---|
| 676 |  | 
|---|
| 677 | set path "[file dirname [info nameof]];.;" | 
|---|
| 678 | if {[info exists env(WINDIR)]} { | 
|---|
| 679 | set windir $env(WINDIR) | 
|---|
| 680 | } | 
|---|
| 681 | if {[info exists windir]} { | 
|---|
| 682 | if {$tcl_platform(os) eq "Windows NT"} { | 
|---|
| 683 | append path "$windir/system32;" | 
|---|
| 684 | } | 
|---|
| 685 | append path "$windir/system;$windir;" | 
|---|
| 686 | } | 
|---|
| 687 |  | 
|---|
| 688 | foreach var {PATH Path path} { | 
|---|
| 689 | if {[info exists env($var)]} { | 
|---|
| 690 | append path ";$env($var)" | 
|---|
| 691 | } | 
|---|
| 692 | } | 
|---|
| 693 |  | 
|---|
| 694 | foreach dir [split $path {;}] { | 
|---|
| 695 | # Skip already checked directories | 
|---|
| 696 | if {[info exists checked($dir)] || ($dir eq {})} { continue } | 
|---|
| 697 | set checked($dir) {} | 
|---|
| 698 | foreach ext $execExtensions { | 
|---|
| 699 | set file [file join $dir ${name}${ext}] | 
|---|
| 700 | if {[file exists $file] && ![file isdirectory $file]} { | 
|---|
| 701 | return [set auto_execs($name) [list $file]] | 
|---|
| 702 | } | 
|---|
| 703 | } | 
|---|
| 704 | } | 
|---|
| 705 | return "" | 
|---|
| 706 | } | 
|---|
| 707 |  | 
|---|
| 708 | } else { | 
|---|
| 709 | # Unix version. | 
|---|
| 710 | # | 
|---|
| 711 | proc auto_execok name { | 
|---|
| 712 | global auto_execs env | 
|---|
| 713 |  | 
|---|
| 714 | if {[info exists auto_execs($name)]} { | 
|---|
| 715 | return $auto_execs($name) | 
|---|
| 716 | } | 
|---|
| 717 | set auto_execs($name) "" | 
|---|
| 718 | if {[llength [file split $name]] != 1} { | 
|---|
| 719 | if {[file executable $name] && ![file isdirectory $name]} { | 
|---|
| 720 | set auto_execs($name) [list $name] | 
|---|
| 721 | } | 
|---|
| 722 | return $auto_execs($name) | 
|---|
| 723 | } | 
|---|
| 724 | foreach dir [split $env(PATH) :] { | 
|---|
| 725 | if {$dir eq ""} { | 
|---|
| 726 | set dir . | 
|---|
| 727 | } | 
|---|
| 728 | set file [file join $dir $name] | 
|---|
| 729 | if {[file executable $file] && ![file isdirectory $file]} { | 
|---|
| 730 | set auto_execs($name) [list $file] | 
|---|
| 731 | return $auto_execs($name) | 
|---|
| 732 | } | 
|---|
| 733 | } | 
|---|
| 734 | return "" | 
|---|
| 735 | } | 
|---|
| 736 |  | 
|---|
| 737 | } | 
|---|
| 738 |  | 
|---|
| 739 | # ::tcl::CopyDirectory -- | 
|---|
| 740 | # | 
|---|
| 741 | # This procedure is called by Tcl's core when attempts to call the | 
|---|
| 742 | # filesystem's copydirectory function fail.  The semantics of the call | 
|---|
| 743 | # are that 'dest' does not yet exist, i.e. dest should become the exact | 
|---|
| 744 | # image of src.  If dest does exist, we throw an error. | 
|---|
| 745 | # | 
|---|
| 746 | # Note that making changes to this procedure can change the results | 
|---|
| 747 | # of running Tcl's tests. | 
|---|
| 748 | # | 
|---|
| 749 | # Arguments: | 
|---|
| 750 | # action -              "renaming" or "copying" | 
|---|
| 751 | # src -                 source directory | 
|---|
| 752 | # dest -                destination directory | 
|---|
| 753 | proc tcl::CopyDirectory {action src dest} { | 
|---|
| 754 | set nsrc [file normalize $src] | 
|---|
| 755 | set ndest [file normalize $dest] | 
|---|
| 756 |  | 
|---|
| 757 | if {$action eq "renaming"} { | 
|---|
| 758 | # Can't rename volumes.  We could give a more precise | 
|---|
| 759 | # error message here, but that would break the test suite. | 
|---|
| 760 | if {$nsrc in [file volumes]} { | 
|---|
| 761 | return -code error "error $action \"$src\" to\ | 
|---|
| 762 | \"$dest\": trying to rename a volume or move a directory\ | 
|---|
| 763 | into itself" | 
|---|
| 764 | } | 
|---|
| 765 | } | 
|---|
| 766 | if {[file exists $dest]} { | 
|---|
| 767 | if {$nsrc eq $ndest} { | 
|---|
| 768 | return -code error "error $action \"$src\" to\ | 
|---|
| 769 | \"$dest\": trying to rename a volume or move a directory\ | 
|---|
| 770 | into itself" | 
|---|
| 771 | } | 
|---|
| 772 | if {$action eq "copying"} { | 
|---|
| 773 | # We used to throw an error here, but, looking more closely | 
|---|
| 774 | # at the core copy code in tclFCmd.c, if the destination | 
|---|
| 775 | # exists, then we should only call this function if -force | 
|---|
| 776 | # is true, which means we just want to over-write.  So, | 
|---|
| 777 | # the following code is now commented out. | 
|---|
| 778 | # | 
|---|
| 779 | # return -code error "error $action \"$src\" to\ | 
|---|
| 780 | # \"$dest\": file already exists" | 
|---|
| 781 | } else { | 
|---|
| 782 | # Depending on the platform, and on the current | 
|---|
| 783 | # working directory, the directories '.', '..' | 
|---|
| 784 | # can be returned in various combinations.  Anyway, | 
|---|
| 785 | # if any other file is returned, we must signal an error. | 
|---|
| 786 | set existing [glob -nocomplain -directory $dest * .*] | 
|---|
| 787 | lappend existing {*}[glob -nocomplain -directory $dest \ | 
|---|
| 788 | -type hidden * .*] | 
|---|
| 789 | foreach s $existing { | 
|---|
| 790 | if {([file tail $s] ne ".") && ([file tail $s] ne "..")} { | 
|---|
| 791 | return -code error "error $action \"$src\" to\ | 
|---|
| 792 | \"$dest\": file already exists" | 
|---|
| 793 | } | 
|---|
| 794 | } | 
|---|
| 795 | } | 
|---|
| 796 | } else { | 
|---|
| 797 | if {[string first $nsrc $ndest] != -1} { | 
|---|
| 798 | set srclen [expr {[llength [file split $nsrc]] -1}] | 
|---|
| 799 | set ndest [lindex [file split $ndest] $srclen] | 
|---|
| 800 | if {$ndest eq [file tail $nsrc]} { | 
|---|
| 801 | return -code error "error $action \"$src\" to\ | 
|---|
| 802 | \"$dest\": trying to rename a volume or move a directory\ | 
|---|
| 803 | into itself" | 
|---|
| 804 | } | 
|---|
| 805 | } | 
|---|
| 806 | file mkdir $dest | 
|---|
| 807 | } | 
|---|
| 808 | # Have to be careful to capture both visible and hidden files. | 
|---|
| 809 | # We will also be more generous to the file system and not | 
|---|
| 810 | # assume the hidden and non-hidden lists are non-overlapping. | 
|---|
| 811 | # | 
|---|
| 812 | # On Unix 'hidden' files begin with '.'.  On other platforms | 
|---|
| 813 | # or filesystems hidden files may have other interpretations. | 
|---|
| 814 | set filelist [concat [glob -nocomplain -directory $src *] \ | 
|---|
| 815 | [glob -nocomplain -directory $src -types hidden *]] | 
|---|
| 816 |  | 
|---|
| 817 | foreach s [lsort -unique $filelist] { | 
|---|
| 818 | if {([file tail $s] ne ".") && ([file tail $s] ne "..")} { | 
|---|
| 819 | file copy -force $s [file join $dest [file tail $s]] | 
|---|
| 820 | } | 
|---|
| 821 | } | 
|---|
| 822 | return | 
|---|
| 823 | } | 
|---|