3
# Default system startup file for Tcl-based applications. Defines
4
# "unknown" procedure and auto-load facilities.
6
# SCCS: @(#) init.tcl 1.95 97/11/19 17:16:34
8
# Copyright (c) 1991-1993 The Regents of the University of California.
9
# Copyright (c) 1994-1996 Sun Microsystems, Inc.
11
# See the file "license.terms" for information on usage and redistribution
12
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15
if {[info commands package] == ""} {
16
error "version mismatch: library\nscripts expect Tcl version 7.5b1 or later but the loaded version is\nonly [info patchlevel]"
18
package require -exact Tcl 8.0
20
# Compute the auto path to use in this interpreter.
21
# (auto_path could be already set, in safe interps for instance)
23
if {![info exists auto_path]} {
24
if [catch {set auto_path $env(TCLLIBPATH)}] {
28
if {[lsearch -exact $auto_path [info library]] < 0} {
29
lappend auto_path [info library]
32
foreach __dir $tcl_pkgPath {
33
if {[lsearch -exact $auto_path $__dir] < 0} {
34
lappend auto_path $__dir
40
# Setup the unknown package handler
42
package unknown tclPkgUnknown
44
# Conditionalize for presence of exec.
46
if {[info commands exec] == ""} {
48
# Some machines, such as the Macintosh, do not have exec. Also, on all
49
# platforms, safe interpreters do not have exec.
56
# Define a log command (which can be overwitten to log errors
57
# differently, specially when stderr is not available)
59
if {[info commands tclLog] == ""} {
60
proc tclLog {string} {
61
catch {puts stderr $string}
65
# The procs defined in this file that have a leading space
66
# are 'hidden' from auto_mkindex because they are not
71
# This procedure is called when a Tcl command is invoked that doesn't
72
# exist in the interpreter. It takes the following steps to make the
75
# 1. See if the autoload facility can locate the command in a
76
# Tcl script file. If so, load it and execute it.
77
# 2. If the command was invoked interactively at top-level:
78
# (a) see if the command exists as an executable UNIX program.
79
# If so, "exec" the command.
80
# (b) see if the command requests csh-like history substitution
81
# in one of the common forms !!, !<number>, or ^old^new. If
82
# so, emulate csh's history substitution.
83
# (c) see if the command is a unique abbreviation for another
84
# command. If so, invoke the command.
87
# args - A list whose elements are the words of the original
88
# command, including the command name.
91
global auto_noexec auto_noload env unknown_pending tcl_interactive
92
global errorCode errorInfo
94
# Save the values of errorCode and errorInfo variables, since they
95
# may get modified if caught errors occur below. The variables will
96
# be restored just before re-executing the missing command.
98
set savedErrorCode $errorCode
99
set savedErrorInfo $errorInfo
100
set name [lindex $args 0]
101
if ![info exists auto_noload] {
103
# Make sure we're not trying to load the same proc twice.
105
if [info exists unknown_pending($name)] {
106
return -code error "self-referential recursion in \"unknown\" for command \"$name\"";
108
set unknown_pending($name) pending;
109
set ret [catch {auto_load $name [uplevel 1 {namespace current}]} msg]
110
unset unknown_pending($name);
112
return -code $ret -errorcode $errorCode \
113
"error while autoloading \"$name\": $msg"
115
if ![array size unknown_pending] {
116
unset unknown_pending
119
set errorCode $savedErrorCode
120
set errorInfo $savedErrorInfo
121
set code [catch {uplevel 1 $args} msg]
124
# Strip the last five lines off the error stack (they're
125
# from the "uplevel" command).
128
set new [split $errorInfo \n]
129
set new [join [lrange $new 0 [expr [llength $new] - 6]] \n]
130
return -code error -errorcode $errorCode \
133
return -code $code $msg
138
if {([info level] == 1) && ([info script] == "") \
139
&& [info exists tcl_interactive] && $tcl_interactive} {
140
if ![info exists auto_noexec] {
141
set new [auto_execok $name]
143
set errorCode $savedErrorCode
144
set errorInfo $savedErrorInfo
146
if {[info commands console] == ""} {
147
set redir ">&@stdout <@stdin"
149
return [uplevel exec $redir $new [lrange $args 1 end]]
152
set errorCode $savedErrorCode
153
set errorInfo $savedErrorInfo
155
set newcmd [history event]
156
} elseif {[regexp {^!(.+)$} $name dummy event]} {
157
set newcmd [history event $event]
158
} elseif {[regexp {^\^([^^]*)\^([^^]*)\^?$} $name dummy old new]} {
159
set newcmd [history event -1]
160
catch {regsub -all -- $old $newcmd $new newcmd}
162
if [info exists newcmd] {
164
history change $newcmd 0
165
return [uplevel $newcmd]
168
set ret [catch {set cmds [info commands $name*]} msg]
169
if {[string compare $name "::"] == 0} {
173
return -code $ret -errorcode $errorCode \
174
"error in unknown while checking if \"$name\" is a unique command abbreviation: $msg"
176
if {[llength $cmds] == 1} {
177
return [uplevel [lreplace $args 0 0 $cmds]]
179
if {[llength $cmds] != 0} {
181
return -code error "empty command name \"\""
184
"ambiguous command name \"$name\": [lsort $cmds]"
188
return -code error "invalid command name \"$name\""
192
# Checks a collection of library directories to see if a procedure
193
# is defined in one of them. If so, it sources the appropriate
194
# library file to create the procedure. Returns 1 if it successfully
195
# loaded the procedure, 0 otherwise.
198
# cmd - Name of the command to find and load.
199
# namespace (optional) The namespace where the command is being used - must be
200
# a canonical namespace as returned [namespace current]
201
# for instance. If not given, namespace current is used.
203
proc auto_load {cmd {namespace {}}} {
204
global auto_index auto_oldpath auto_path env errorInfo errorCode
206
if {[string length $namespace] == 0} {
207
set namespace [uplevel {namespace current}]
209
set nameList [auto_qualify $cmd $namespace]
210
# workaround non canonical auto_index entries that might be around
211
# from older auto_mkindex versions
212
lappend nameList $cmd
213
foreach name $nameList {
214
if [info exists auto_index($name)] {
215
uplevel #0 $auto_index($name)
216
return [expr {[info commands $name] != ""}]
219
if ![info exists auto_path] {
222
if [info exists auto_oldpath] {
223
if {$auto_oldpath == $auto_path} {
227
set auto_oldpath $auto_path
229
# Check if we are a safe interpreter. In that case, we support only
230
# newer format tclIndex files.
232
set issafe [interp issafe]
233
for {set i [expr [llength $auto_path] - 1]} {$i >= 0} {incr i -1} {
234
set dir [lindex $auto_path $i]
237
catch {source [file join $dir tclIndex]}
238
} elseif [catch {set f [open [file join $dir tclIndex]]}] {
243
if {$id == "# Tcl autoload index file, version 2.0"} {
246
"# Tcl autoload index file: each line identifies a Tcl"} {
247
while {[gets $f line] >= 0} {
248
if {([string index $line 0] == "#")
249
|| ([llength $line] != 2)} {
252
set name [lindex $line 0]
253
set auto_index($name) \
254
"source [file join $dir [lindex $line 1]]"
258
"[file join $dir tclIndex] isn't a proper Tcl index file"
265
error $msg $errorInfo $errorCode
269
foreach name $nameList {
270
if [info exists auto_index($name)] {
271
uplevel #0 $auto_index($name)
272
if {[info commands $name] != ""} {
281
# compute a fully qualified names list for use in the auto_index array.
282
# For historical reasons, commands in the global namespace do not have leading
283
# :: in the index key. The list has two elements when the command name is
284
# relative (no leading ::) and the namespace is not the global one. Otherwise
285
# only one name is returned (and searched in the auto_index).
288
# cmd The command name. Can be any name accepted for command
289
# invocations (Like "foo::::bar").
290
# namespace The namespace where the command is being used - must be
291
# a canonical namespace as returned by [namespace current]
294
proc auto_qualify {cmd namespace} {
296
# count separators and clean them up
297
# (making sure that foo:::::bar will be treated as foo::bar)
298
set n [regsub -all {::+} $cmd :: cmd]
300
# Ignore namespace if the name starts with ::
301
# Handle special case of only leading ::
303
# Before each return case we give an example of which category it is
304
# with the following form :
305
# ( inputCmd, inputNameSpace) -> output
307
if {[regexp {^::(.*)$} $cmd x tail]} {
309
# ( ::foo::bar , * ) -> ::foo::bar
312
# ( ::global , * ) -> global
317
# Potentially returning 2 elements to try :
318
# (if the current namespace is not the global one)
321
if {[string compare $namespace ::] == 0} {
322
# ( nocolons , :: ) -> nocolons
325
# ( nocolons , ::sub ) -> ::sub::nocolons nocolons
326
return [list ${namespace}::$cmd $cmd]
329
if {[string compare $namespace ::] == 0} {
330
# ( foo::bar , :: ) -> ::foo::bar
333
# ( foo::bar , ::sub ) -> ::sub::foo::bar ::foo::bar
334
return [list ${namespace}::$cmd ::$cmd]
339
if {[string compare $tcl_platform(platform) windows] == 0} {
343
# Returns string that indicates name of program to execute if
344
# name corresponds to a shell builtin or an executable in the
345
# Windows search path, or "" otherwise. Builds an associative
346
# array auto_execs that caches information about previous checks,
350
# name - Name of a command.
354
# Note that info executable doesn't work under Windows, so we have to
355
# look for files with .exe, .com, or .bat extensions. Also, the path
356
# may be in the Path or PATH environment variables, and path
357
# components are separated with semicolons, not colons as under Unix.
359
proc auto_execok name {
360
global auto_execs env tcl_platform
362
if [info exists auto_execs($name)] {
363
return $auto_execs($name)
365
set auto_execs($name) ""
367
if {[lsearch -exact {cls copy date del erase dir echo mkdir md rename
368
ren rmdir rd time type ver vol} $name] != -1} {
369
return [set auto_execs($name) [list $env(COMSPEC) /c $name]]
372
if {[llength [file split $name]] != 1} {
373
foreach ext {{} .com .exe .bat} {
374
set file ${name}${ext}
375
if {[file exists $file] && ![file isdirectory $file]} {
376
return [set auto_execs($name) [list $file]]
382
set path "[file dirname [info nameof]];.;"
383
if {[info exists env(WINDIR)]} {
384
set windir $env(WINDIR)
386
if {[info exists windir]} {
387
if {$tcl_platform(os) == "Windows NT"} {
388
append path "$windir/system32;"
390
append path "$windir/system;$windir;"
393
if {[info exists env(PATH)]} {
394
append path $env(PATH)
397
foreach dir [split $path {;}] {
401
foreach ext {{} .com .exe .bat} {
402
set file [file join $dir ${name}${ext}]
403
if {[file exists $file] && ![file isdirectory $file]} {
404
return [set auto_execs($name) [list $file]]
415
# Returns string that indicates name of program to execute if
416
# name corresponds to an executable in the path. Builds an associative
417
# array auto_execs that caches information about previous checks,
421
# name - Name of a command.
425
proc auto_execok name {
426
global auto_execs env
428
if [info exists auto_execs($name)] {
429
return $auto_execs($name)
431
set auto_execs($name) ""
432
if {[llength [file split $name]] != 1} {
433
if {[file executable $name] && ![file isdirectory $name]} {
434
set auto_execs($name) [list $name]
436
return $auto_execs($name)
438
foreach dir [split $env(PATH) :] {
442
set file [file join $dir $name]
443
if {[file executable $file] && ![file isdirectory $file]} {
444
set auto_execs($name) [list $file]
445
return $auto_execs($name)
453
# Destroy all cached information for auto-loading and auto-execution,
454
# so that the information gets recomputed the next time it's needed.
455
# Also delete any procedures that are listed in the auto-load index
456
# except those defined in this file.
462
global auto_execs auto_index auto_oldpath
463
foreach p [info procs] {
464
if {[info exists auto_index($p)] && ![string match auto_* $p]
465
&& ([lsearch -exact {unknown pkg_mkIndex tclPkgSetup
466
tclMacPkgSearch tclPkgUnknown} $p] < 0)} {
470
catch {unset auto_execs}
471
catch {unset auto_index}
472
catch {unset auto_oldpath}
476
# Regenerate a tclIndex file from Tcl source files. Takes as argument
477
# the name of the directory in which the tclIndex file is to be placed,
478
# followed by any number of glob patterns to use in that directory to
479
# locate all of the relevant files. It does not parse or source the file
480
# so the generated index will not contain the appropriate namespace qualifiers
481
# if you don't explicitly specify it.
484
# dir - Name of the directory in which to create an index.
485
# args - Any number of additional arguments giving the
486
# names of files within dir. If no additional
487
# are given auto_mkindex will look for *.tcl.
489
proc auto_mkindex {dir args} {
490
global errorCode errorInfo
494
append index "# Tcl autoload index file, version 2.0\n"
495
append index "# This file is generated by the \"auto_mkindex\" command\n"
496
append index "# and sourced to set up indexing information for one or\n"
497
append index "# more commands. Typically each line is a command that\n"
498
append index "# sets an element in the auto_index array, where the\n"
499
append index "# element name is the name of a command and the value is\n"
500
append index "# a script that loads the command.\n\n"
504
foreach file [eval glob $args] {
508
while {[gets $f line] >= 0} {
509
if [regexp {^proc[ ]+([^ ]*)} $line match procName] {
510
set procName [lindex [auto_qualify $procName "::"] 0]
511
append index "set [list auto_index($procName)]"
512
append index " \[list source \[file join \$dir [list $file]\]\]\n"
522
error $msg $info $code
527
set f [open tclIndex w]
528
puts $f $index nonewline
537
error $msg $info $code
542
# This procedure creates a package index in a given directory. The
543
# package index consists of a "pkgIndex.tcl" file whose contents are
544
# a Tcl script that sets up package information with "package require"
545
# commands. The commands describe all of the packages defined by the
546
# files given as arguments.
549
# dir - Name of the directory in which to create the index.
550
# args - Any number of additional arguments, each giving
551
# a glob pattern that matches the names of one or
552
# more shared libraries or Tcl script files in
555
proc pkg_mkIndex {dir args} {
556
global errorCode errorInfo
557
if {[llength $args] == 0} {
558
return -code error "wrong # args: should be\
559
\"pkg_mkIndex dir pattern ?pattern ...?\"";
561
append index "# Tcl package index file, version 1.0\n"
562
append index "# This file is generated by the \"pkg_mkIndex\" command\n"
563
append index "# and sourced either when an application starts up or\n"
564
append index "# by a \"package unknown\" script. It invokes the\n"
565
append index "# \"package ifneeded\" command to set up package-related\n"
566
append index "# information so that packages will be loaded automatically\n"
567
append index "# in response to \"package require\" commands. When this\n"
568
append index "# script is sourced, the variable \$dir must contain the\n"
569
append index "# full path name of this file's directory.\n"
572
foreach file [eval glob $args] {
573
# For each file, figure out what commands and packages it provides.
574
# To do this, create a child interpreter, load the file into the
575
# interpreter, and get a list of the new commands and packages
576
# that are defined. Define an empty "package unknown" script so
577
# that there are no recursive package inclusions.
579
set c [interp create]
581
# If Tk is loaded in the parent interpreter, load it into the
582
# child also, in case the extension depends on it.
584
foreach pkg [info loaded] {
585
if {[lindex $pkg 1] == "Tk"} {
586
$c eval {set argv {-geometry +0+0}}
587
load [lindex $pkg 0] Tk $c
591
$c eval [list set file $file]
595
rename package package-orig
596
proc package {what args} {
598
require { return ; # ignore transitive requires }
599
default { eval package-orig {$what} $args }
602
proc pkgGetAllNamespaces {{root {}}} {
604
foreach ns [namespace children $root] {
605
eval lappend list [pkgGetAllNamespaces $ns]
609
package unknown dummy
610
set origCmds [info commands]
611
set dir "" ;# in case file is pkgIndex.tcl
614
# Try to load the file if it has the shared library extension,
615
# otherwise source it. It's important not to try to load
616
# files that aren't shared libraries, because on some systems
617
# (like SunOS) the loader will abort the whole application
618
# when it gets an error.
620
if {[string compare [file extension $file] \
621
[info sharedlibextension]] == 0} {
623
# The "file join ." command below is necessary. Without
624
# it, if the file name has no \'s and we're on UNIX, the
625
# load command will invoke the LD_LIBRARY_PATH search
626
# mechanism, which could cause the wrong file to be used.
628
load [file join . $file]
634
foreach ns [pkgGetAllNamespaces] {
635
namespace import ${ns}::*
637
foreach i [info commands] {
640
foreach i $origCmds {
641
catch {unset cmds($i)}
644
foreach i [array names cmds] {
645
# reverse engineer which namespace a command comes from
646
set absolute [namespace origin $i]
647
if {[string compare ::$i $absolute] != 0} {
648
set cmds($absolute) 1
652
foreach i [package names] {
653
if {([string compare [package provide $i] ""] != 0)
654
&& ([string compare $i Tcl] != 0)
655
&& ([string compare $i Tk] != 0)} {
656
lappend pkgs [list $i [package provide $i]]
661
tclLog "error while loading or sourcing $file: $msg"
663
foreach pkg [$c eval set pkgs] {
664
lappend files($pkg) [list $file [$c eval set type] \
665
[lsort [$c eval array names cmds]]]
669
foreach pkg [lsort [array names files]] {
670
append index "\npackage ifneeded $pkg\
671
\[list tclPkgSetup \$dir [lrange $pkg 0 0] [lrange $pkg 1 1]\
672
[list $files($pkg)]\]"
674
set f [open pkgIndex.tcl w]
681
# This is a utility procedure use by pkgIndex.tcl files. It is invoked
682
# as part of a "package ifneeded" script. It calls "package provide"
683
# to indicate that a package is available, then sets entries in the
684
# auto_index array so that the package's files will be auto-loaded when
685
# the commands are used.
688
# dir - Directory containing all the files for this package.
689
# pkg - Name of the package (no version number).
690
# version - Version number for the package, such as 2.1.3.
691
# files - List of files that constitute the package. Each
692
# element is a sub-list with three elements. The first
693
# is the name of a file relative to $dir, the second is
694
# "load" or "source", indicating whether the file is a
695
# loadable binary or a script to source, and the third
696
# is a list of commands defined by this file.
698
proc tclPkgSetup {dir pkg version files} {
701
package provide $pkg $version
702
foreach fileInfo $files {
703
set f [lindex $fileInfo 0]
704
set type [lindex $fileInfo 1]
705
foreach cmd [lindex $fileInfo 2] {
706
if {$type == "load"} {
707
set auto_index($cmd) [list load [file join $dir $f] $pkg]
709
set auto_index($cmd) [list source [file join $dir $f]]
716
# The procedure is used on the Macintosh to search a given directory for files
717
# with a TEXT resource named "pkgIndex". If it exists it is sourced in to the
718
# interpreter to setup the package database.
720
proc tclMacPkgSearch {dir} {
721
foreach x [glob -nocomplain [file join $dir *.shlb]] {
722
if [file isfile $x] {
723
set res [resource open $x]
724
foreach y [resource list TEXT $res] {
725
if {$y == "pkgIndex"} {source -rsrc pkgIndex}
727
catch {resource close $res}
733
# This procedure provides the default for the "package unknown" function.
734
# It is invoked when a package that's needed can't be found. It scans
735
# the auto_path directories and their immediate children looking for
736
# pkgIndex.tcl files and sources any such files that are found to setup
737
# the package database. (On the Macintosh we also search for pkgIndex
738
# TEXT resources in all files.)
741
# name - Name of desired package. Not used.
742
# version - Version of desired package. Not used.
743
# exact - Either "-exact" or omitted. Not used.
745
proc tclPkgUnknown {name version {exact {}}} {
746
global auto_path tcl_platform env
748
if ![info exists auto_path] {
751
for {set i [expr [llength $auto_path] - 1]} {$i >= 0} {incr i -1} {
752
# we can't use glob in safe interps, so enclose the following
753
# in a catch statement
755
foreach file [glob -nocomplain [file join [lindex $auto_path $i] \
757
set dir [file dirname $file]
758
if [catch {source $file} msg] {
759
tclLog "error reading package index file $file: $msg"
763
set dir [lindex $auto_path $i]
764
set file [file join $dir pkgIndex.tcl]
765
# safe interps usually don't have "file readable", nor stderr channel
766
if {[interp issafe] || [file readable $file]} {
767
if {[catch {source $file} msg] && ![interp issafe]} {
768
tclLog "error reading package index file $file: $msg"
771
# On the Macintosh we also look in the resource fork
772
# of shared libraries
773
# We can't use tclMacPkgSearch in safe interps because it uses glob
774
if {(![interp issafe]) && ($tcl_platform(platform) == "macintosh")} {
775
set dir [lindex $auto_path $i]
777
foreach x [glob -nocomplain [file join $dir *]] {
778
if [file isdirectory $x] {