~ubuntu-branches/ubuntu/warty/electric/warty

« back to all changes in this revision

Viewing changes to lib/tcl/init.tcl

  • Committer: Bazaar Package Importer
  • Author(s): Chris Ruffin
  • Date: 2002-03-23 11:02:56 UTC
  • Revision ID: james.westby@ubuntu.com-20020323110256-mx008emo1nb2k11i
Tags: 6.05-1
* new upstream release
* added menu hints (closes: #128765)
* changed doc-base to go into Technical section per menu-policy

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# init.tcl --
 
2
#
 
3
# Default system startup file for Tcl-based applications.  Defines
 
4
# "unknown" procedure and auto-load facilities.
 
5
#
 
6
# SCCS: @(#) init.tcl 1.95 97/11/19 17:16:34
 
7
#
 
8
# Copyright (c) 1991-1993 The Regents of the University of California.
 
9
# Copyright (c) 1994-1996 Sun Microsystems, Inc.
 
10
#
 
11
# See the file "license.terms" for information on usage and redistribution
 
12
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 
13
#
 
14
 
 
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]"
 
17
}
 
18
package require -exact Tcl 8.0
 
19
 
 
20
# Compute the auto path to use in this interpreter.
 
21
# (auto_path could be already set, in safe interps for instance)
 
22
 
 
23
if {![info exists auto_path]} {
 
24
    if [catch {set auto_path $env(TCLLIBPATH)}] {
 
25
        set auto_path ""
 
26
    }
 
27
}
 
28
if {[lsearch -exact $auto_path [info library]] < 0} {
 
29
    lappend auto_path [info library]
 
30
}
 
31
catch {
 
32
    foreach __dir $tcl_pkgPath {
 
33
        if {[lsearch -exact $auto_path $__dir] < 0} {
 
34
            lappend auto_path $__dir
 
35
        }
 
36
    }
 
37
    unset __dir
 
38
}
 
39
 
 
40
# Setup the unknown package handler
 
41
 
 
42
package unknown tclPkgUnknown
 
43
 
 
44
# Conditionalize for presence of exec.
 
45
 
 
46
if {[info commands exec] == ""} {
 
47
 
 
48
    # Some machines, such as the Macintosh, do not have exec. Also, on all
 
49
    # platforms, safe interpreters do not have exec.
 
50
 
 
51
    set auto_noexec 1
 
52
}
 
53
set errorCode ""
 
54
set errorInfo ""
 
55
 
 
56
# Define a log command (which can be overwitten to log errors
 
57
# differently, specially when stderr is not available)
 
58
 
 
59
if {[info commands tclLog] == ""} {
 
60
    proc tclLog {string} {
 
61
        catch {puts stderr $string}
 
62
    }
 
63
}
 
64
 
 
65
# The procs defined in this file that have a leading space
 
66
# are 'hidden' from auto_mkindex because they are not
 
67
# auto-loadable.
 
68
 
 
69
 
 
70
# unknown --
 
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
 
73
# command available:
 
74
#
 
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.
 
85
#
 
86
# Arguments:
 
87
# args -        A list whose elements are the words of the original
 
88
#               command, including the command name.
 
89
 
 
90
 proc unknown args {
 
91
    global auto_noexec auto_noload env unknown_pending tcl_interactive
 
92
    global errorCode errorInfo
 
93
 
 
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.
 
97
 
 
98
    set savedErrorCode $errorCode
 
99
    set savedErrorInfo $errorInfo
 
100
    set name [lindex $args 0]
 
101
    if ![info exists auto_noload] {
 
102
        #
 
103
        # Make sure we're not trying to load the same proc twice.
 
104
        #
 
105
        if [info exists unknown_pending($name)] {
 
106
            return -code error "self-referential recursion in \"unknown\" for command \"$name\"";
 
107
        }
 
108
        set unknown_pending($name) pending;
 
109
        set ret [catch {auto_load $name [uplevel 1 {namespace current}]} msg]
 
110
        unset unknown_pending($name);
 
111
        if {$ret != 0} {
 
112
            return -code $ret -errorcode $errorCode \
 
113
                "error while autoloading \"$name\": $msg"
 
114
        }
 
115
        if ![array size unknown_pending] {
 
116
            unset unknown_pending
 
117
        }
 
118
        if $msg {
 
119
            set errorCode $savedErrorCode
 
120
            set errorInfo $savedErrorInfo
 
121
            set code [catch {uplevel 1 $args} msg]
 
122
            if {$code ==  1} {
 
123
                #
 
124
                # Strip the last five lines off the error stack (they're
 
125
                # from the "uplevel" command).
 
126
                #
 
127
 
 
128
                set new [split $errorInfo \n]
 
129
                set new [join [lrange $new 0 [expr [llength $new] - 6]] \n]
 
130
                return -code error -errorcode $errorCode \
 
131
                        -errorinfo $new $msg
 
132
            } else {
 
133
                return -code $code $msg
 
134
            }
 
135
        }
 
136
    }
 
137
 
 
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]
 
142
            if {$new != ""} {
 
143
                set errorCode $savedErrorCode
 
144
                set errorInfo $savedErrorInfo
 
145
                set redir ""
 
146
                if {[info commands console] == ""} {
 
147
                    set redir ">&@stdout <@stdin"
 
148
                }
 
149
                return [uplevel exec $redir $new [lrange $args 1 end]]
 
150
            }
 
151
        }
 
152
        set errorCode $savedErrorCode
 
153
        set errorInfo $savedErrorInfo
 
154
        if {$name == "!!"} {
 
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}
 
161
        }
 
162
        if [info exists newcmd] {
 
163
            tclLog $newcmd
 
164
            history change $newcmd 0
 
165
            return [uplevel $newcmd]
 
166
        }
 
167
 
 
168
        set ret [catch {set cmds [info commands $name*]} msg]
 
169
        if {[string compare $name "::"] == 0} {
 
170
            set name ""
 
171
        }
 
172
        if {$ret != 0} {
 
173
            return -code $ret -errorcode $errorCode \
 
174
                "error in unknown while checking if \"$name\" is a unique command abbreviation: $msg"
 
175
        }
 
176
        if {[llength $cmds] == 1} {
 
177
            return [uplevel [lreplace $args 0 0 $cmds]]
 
178
        }
 
179
        if {[llength $cmds] != 0} {
 
180
            if {$name == ""} {
 
181
                return -code error "empty command name \"\""
 
182
            } else {
 
183
                return -code error \
 
184
                        "ambiguous command name \"$name\": [lsort $cmds]"
 
185
            }
 
186
        }
 
187
    }
 
188
    return -code error "invalid command name \"$name\""
 
189
}
 
190
 
 
191
# auto_load --
 
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.
 
196
#
 
197
# Arguments: 
 
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.
 
202
 
 
203
 proc auto_load {cmd {namespace {}}} {
 
204
    global auto_index auto_oldpath auto_path env errorInfo errorCode
 
205
 
 
206
    if {[string length $namespace] == 0} {
 
207
        set namespace [uplevel {namespace current}]
 
208
    }
 
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] != ""}]
 
217
        }
 
218
    }
 
219
    if ![info exists auto_path] {
 
220
        return 0
 
221
    }
 
222
    if [info exists auto_oldpath] {
 
223
        if {$auto_oldpath == $auto_path} {
 
224
            return 0
 
225
        }
 
226
    }
 
227
    set auto_oldpath $auto_path
 
228
 
 
229
    # Check if we are a safe interpreter. In that case, we support only
 
230
    # newer format tclIndex files.
 
231
 
 
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]
 
235
        set f ""
 
236
        if {$issafe} {
 
237
            catch {source [file join $dir tclIndex]}
 
238
        } elseif [catch {set f [open [file join $dir tclIndex]]}] {
 
239
            continue
 
240
        } else {
 
241
            set error [catch {
 
242
                set id [gets $f]
 
243
                if {$id == "# Tcl autoload index file, version 2.0"} {
 
244
                    eval [read $f]
 
245
                } elseif {$id == \
 
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)} {
 
250
                            continue
 
251
                        }
 
252
                        set name [lindex $line 0]
 
253
                        set auto_index($name) \
 
254
                            "source [file join $dir [lindex $line 1]]"
 
255
                    }
 
256
                } else {
 
257
                    error \
 
258
                      "[file join $dir tclIndex] isn't a proper Tcl index file"
 
259
                }
 
260
            } msg]
 
261
            if {$f != ""} {
 
262
                close $f
 
263
            }
 
264
            if $error {
 
265
                error $msg $errorInfo $errorCode
 
266
            }
 
267
        }
 
268
    }
 
269
    foreach name $nameList {
 
270
        if [info exists auto_index($name)] {
 
271
            uplevel #0 $auto_index($name)
 
272
            if {[info commands $name] != ""} {
 
273
                return 1
 
274
            }
 
275
        }
 
276
    }
 
277
    return 0
 
278
}
 
279
 
 
280
# auto_qualify --
 
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).
 
286
#
 
287
# Arguments -
 
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]
 
292
#               for instance.
 
293
 
 
294
 proc auto_qualify {cmd namespace} {
 
295
 
 
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]
 
299
 
 
300
    # Ignore namespace if the name starts with ::
 
301
    # Handle special case of only leading ::
 
302
 
 
303
    # Before each return case we give an example of which category it is
 
304
    # with the following form :
 
305
    # ( inputCmd, inputNameSpace) -> output
 
306
 
 
307
    if {[regexp {^::(.*)$} $cmd x tail]} {
 
308
        if {$n > 1} {
 
309
            # ( ::foo::bar , * ) -> ::foo::bar
 
310
            return [list $cmd]
 
311
        } else {
 
312
            # ( ::global , * ) -> global
 
313
            return [list $tail]
 
314
        }
 
315
    }
 
316
    
 
317
    # Potentially returning 2 elements to try  :
 
318
    # (if the current namespace is not the global one)
 
319
 
 
320
    if {$n == 0} {
 
321
        if {[string compare $namespace ::] == 0} {
 
322
            # ( nocolons , :: ) -> nocolons
 
323
            return [list $cmd]
 
324
        } else {
 
325
            # ( nocolons , ::sub ) -> ::sub::nocolons nocolons
 
326
            return [list ${namespace}::$cmd $cmd]
 
327
        }
 
328
    } else {
 
329
        if {[string compare $namespace ::] == 0} {
 
330
            #  ( foo::bar , :: ) -> ::foo::bar
 
331
            return [list ::$cmd]
 
332
        } else {
 
333
            # ( foo::bar , ::sub ) -> ::sub::foo::bar ::foo::bar
 
334
            return [list ${namespace}::$cmd ::$cmd]
 
335
        }
 
336
    }
 
337
}
 
338
 
 
339
if {[string compare $tcl_platform(platform) windows] == 0} {
 
340
 
 
341
# auto_execok --
 
342
#
 
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, 
 
347
# for speed.
 
348
#
 
349
# Arguments: 
 
350
# name -                        Name of a command.
 
351
 
 
352
# Windows version.
 
353
#
 
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.
 
358
#
 
359
proc auto_execok name {
 
360
    global auto_execs env tcl_platform
 
361
 
 
362
    if [info exists auto_execs($name)] {
 
363
        return $auto_execs($name)
 
364
    }
 
365
    set auto_execs($name) ""
 
366
 
 
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]]
 
370
    }
 
371
 
 
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]]
 
377
            }
 
378
        }
 
379
        return ""
 
380
    }
 
381
 
 
382
    set path "[file dirname [info nameof]];.;"
 
383
    if {[info exists env(WINDIR)]} {
 
384
        set windir $env(WINDIR) 
 
385
    }
 
386
    if {[info exists windir]} {
 
387
        if {$tcl_platform(os) == "Windows NT"} {
 
388
            append path "$windir/system32;"
 
389
        }
 
390
        append path "$windir/system;$windir;"
 
391
    }
 
392
 
 
393
    if {[info exists env(PATH)]} {
 
394
        append path $env(PATH)
 
395
    }
 
396
 
 
397
    foreach dir [split $path {;}] {
 
398
        if {$dir == ""} {
 
399
            set dir .
 
400
        }
 
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]]
 
405
            }
 
406
        }
 
407
    }
 
408
    return ""
 
409
}
 
410
 
 
411
} else {
 
412
 
 
413
# auto_execok --
 
414
#
 
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, 
 
418
# for speed.
 
419
#
 
420
# Arguments: 
 
421
# name -                        Name of a command.
 
422
 
 
423
# Unix version.
 
424
#
 
425
proc auto_execok name {
 
426
    global auto_execs env
 
427
 
 
428
    if [info exists auto_execs($name)] {
 
429
        return $auto_execs($name)
 
430
    }
 
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]
 
435
        }
 
436
        return $auto_execs($name)
 
437
    }
 
438
    foreach dir [split $env(PATH) :] {
 
439
        if {$dir == ""} {
 
440
            set dir .
 
441
        }
 
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)
 
446
        }
 
447
    }
 
448
    return ""
 
449
}
 
450
 
 
451
}
 
452
# auto_reset --
 
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.
 
457
#
 
458
# Arguments: 
 
459
# None.
 
460
 
 
461
proc auto_reset {} {
 
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)} {
 
467
            rename $p {}
 
468
        }
 
469
    }
 
470
    catch {unset auto_execs}
 
471
    catch {unset auto_index}
 
472
    catch {unset auto_oldpath}
 
473
}
 
474
 
 
475
# auto_mkindex --
 
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.
 
482
#
 
483
# Arguments: 
 
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.
 
488
 
 
489
proc auto_mkindex {dir args} {
 
490
    global errorCode errorInfo
 
491
    set oldDir [pwd]
 
492
    cd $dir
 
493
    set dir [pwd]
 
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"
 
501
    if {$args == ""} {
 
502
        set args *.tcl
 
503
    }
 
504
    foreach file [eval glob $args] {
 
505
        set f ""
 
506
        set error [catch {
 
507
            set f [open $file]
 
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"
 
513
                }
 
514
            }
 
515
            close $f
 
516
        } msg]
 
517
        if $error {
 
518
            set code $errorCode
 
519
            set info $errorInfo
 
520
            catch {close $f}
 
521
            cd $oldDir
 
522
            error $msg $info $code
 
523
        }
 
524
    }
 
525
    set f ""
 
526
    set error [catch {
 
527
        set f [open tclIndex w]
 
528
        puts $f $index nonewline
 
529
        close $f
 
530
        cd $oldDir
 
531
    } msg]
 
532
    if $error {
 
533
        set code $errorCode
 
534
        set info $errorInfo
 
535
        catch {close $f}
 
536
        cd $oldDir
 
537
        error $msg $info $code
 
538
    }
 
539
}
 
540
 
 
541
# pkg_mkIndex --
 
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.
 
547
#
 
548
# 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
 
553
#                       dir.
 
554
 
 
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 ...?\"";
 
560
    }
 
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"
 
570
    set oldDir [pwd]
 
571
    cd $dir
 
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.
 
578
 
 
579
        set c [interp create]
 
580
 
 
581
        # If Tk is loaded in the parent interpreter, load it into the
 
582
        # child also, in case the extension depends on it.
 
583
 
 
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
 
588
                break
 
589
            }
 
590
        }
 
591
        $c eval [list set file $file]
 
592
        if [catch {
 
593
            $c eval {
 
594
                proc dummy args {}
 
595
                rename package package-orig
 
596
                proc package {what args} {
 
597
                    switch -- $what {
 
598
                        require { return ; # ignore transitive requires }
 
599
                        default { eval package-orig {$what} $args }
 
600
                    }
 
601
                }
 
602
                proc pkgGetAllNamespaces {{root {}}} {
 
603
                    set list $root
 
604
                    foreach ns [namespace children $root] {
 
605
                        eval lappend list [pkgGetAllNamespaces $ns]
 
606
                    }
 
607
                    return $list
 
608
                }
 
609
                package unknown dummy
 
610
                set origCmds [info commands]
 
611
                set dir ""              ;# in case file is pkgIndex.tcl
 
612
                set pkgs ""
 
613
 
 
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.
 
619
 
 
620
                if {[string compare [file extension $file] \
 
621
                        [info sharedlibextension]] == 0} {
 
622
 
 
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.
 
627
 
 
628
                    load [file join . $file]
 
629
                    set type load
 
630
                } else {
 
631
                    source $file
 
632
                    set type source
 
633
                }
 
634
                foreach ns [pkgGetAllNamespaces] {
 
635
                    namespace import ${ns}::*
 
636
                }
 
637
                foreach i [info commands] {
 
638
                    set cmds($i) 1
 
639
                }
 
640
                foreach i $origCmds {
 
641
                    catch {unset cmds($i)}
 
642
 
 
643
                }
 
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
 
649
                        unset cmds($i)
 
650
                    }
 
651
                }
 
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]]
 
657
                    }
 
658
                }
 
659
            }
 
660
        } msg] {
 
661
            tclLog "error while loading or sourcing $file: $msg"
 
662
        }
 
663
        foreach pkg [$c eval set pkgs] {
 
664
            lappend files($pkg) [list $file [$c eval set type] \
 
665
                    [lsort [$c eval array names cmds]]]
 
666
        }
 
667
        interp delete $c
 
668
    }
 
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)]\]"
 
673
    }
 
674
    set f [open pkgIndex.tcl w]
 
675
    puts $f $index
 
676
    close $f
 
677
    cd $oldDir
 
678
}
 
679
 
 
680
# tclPkgSetup --
 
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.
 
686
#
 
687
# Arguments:
 
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.
 
697
 
 
698
proc tclPkgSetup {dir pkg version files} {
 
699
    global auto_index
 
700
 
 
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]
 
708
            } else {
 
709
                set auto_index($cmd) [list source [file join $dir $f]]
 
710
            } 
 
711
        }
 
712
    }
 
713
}
 
714
 
 
715
# tclMacPkgSearch --
 
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.
 
719
 
 
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}
 
726
            }
 
727
            catch {resource close $res}
 
728
        }
 
729
    }
 
730
}
 
731
 
 
732
# tclPkgUnknown --
 
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.)
 
739
#
 
740
# Arguments:
 
741
# name -                Name of desired package.  Not used.
 
742
# version -             Version of desired package.  Not used.
 
743
# exact -               Either "-exact" or omitted.  Not used.
 
744
 
 
745
proc tclPkgUnknown {name version {exact {}}} {
 
746
    global auto_path tcl_platform env
 
747
 
 
748
    if ![info exists auto_path] {
 
749
        return
 
750
    }
 
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
 
754
        catch {
 
755
            foreach file [glob -nocomplain [file join [lindex $auto_path $i] \
 
756
                    * pkgIndex.tcl]] {
 
757
                set dir [file dirname $file]
 
758
                if [catch {source $file} msg] {
 
759
                    tclLog "error reading package index file $file: $msg"
 
760
                }
 
761
            }
 
762
        }
 
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"
 
769
            }
 
770
        }
 
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]
 
776
            tclMacPkgSearch $dir
 
777
            foreach x [glob -nocomplain [file join $dir *]] {
 
778
                if [file isdirectory $x] {
 
779
                    set dir $x
 
780
                    tclMacPkgSearch $dir
 
781
                }
 
782
            }
 
783
        }
 
784
    }
 
785
}