OSDN Git Service

Initial revision
[pf3gnuchains/pf3gnuchains3x.git] / tcl / library / auto.tcl
1 # auto.tcl --
2 #
3 # utility procs formerly in init.tcl dealing with auto execution
4 # of commands and can be auto loaded themselves.
5 #
6 # RCS: @(#) $Id$
7 #
8 # Copyright (c) 1991-1993 The Regents of the University of California.
9 # Copyright (c) 1994-1998 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 # auto_reset --
16 #
17 # Destroy all cached information for auto-loading and auto-execution,
18 # so that the information gets recomputed the next time it's needed.
19 # Also delete any procedures that are listed in the auto-load index
20 # except those defined in this file.
21 #
22 # Arguments: 
23 # None.
24
25 proc auto_reset {} {
26     global auto_execs auto_index auto_oldpath
27     foreach p [info procs] {
28         if {[info exists auto_index($p)] && ![string match auto_* $p]
29                 && ([lsearch -exact {unknown pkg_mkIndex tclPkgSetup
30                         tcl_findLibrary pkg_compareExtension
31                         tclMacPkgSearch tclPkgUnknown} $p] < 0)} {
32             rename $p {}
33         }
34     }
35     catch {unset auto_execs}
36     catch {unset auto_index}
37     catch {unset auto_oldpath}
38 }
39
40 # tcl_findLibrary --
41 #
42 #       This is a utility for extensions that searches for a library directory
43 #       using a canonical searching algorithm. A side effect is to source
44 #       the initialization script and set a global library variable.
45 #
46 # Arguments:
47 #       basename        Prefix of the directory name, (e.g., "tk")
48 #       version         Version number of the package, (e.g., "8.0")
49 #       patch           Patchlevel of the package, (e.g., "8.0.3")
50 #       initScript      Initialization script to source (e.g., tk.tcl)
51 #       enVarName       environment variable to honor (e.g., TK_LIBRARY)
52 #       varName         Global variable to set when done (e.g., tk_library)
53 #       CYGNUS LOCAL:   We have funny things like gdb having different library
54 #                       names before & after install (and neither of them is gdb
55 #                       or gdb$version... 
56 #       srcLibName      The name of the library directory in the build tree (assumed to be 
57 #                       under the basename directory.
58 #       instLibName     The name of the installed library directory
59 #       pkgName         The package name (for cases like Itcl where you have
60 #                       several subpackages under one package...
61 #       debug_startup   Run the startup proc through debugger_eval?
62
63 proc tcl_findLibrary {basename version patch initScript 
64                       enVarName varName {srcLibName {}} {instLibName {}} 
65                       {pkgName {}} {debug_startup 0}} {
66     upvar #0 $varName the_library
67     global env errorInfo
68
69     set dirs {}
70     set errors {}
71     # The C application may have hardwired a path, which we honor
72     
73     if {[info exist the_library] && [string compare $the_library {}]} {
74         lappend dirs $the_library
75     } else {
76
77         # Do the canonical search
78
79         # 1. From an environment variable, if it exists
80
81         if {[info exists env($enVarName)]} {
82             lappend dirs $env($enVarName)
83         }
84
85         # 2. Relative to the Tcl library
86        
87         if {$srcLibName == ""} {
88           set srcLibName library
89         }
90         if {$instLibName == ""} {
91           set instLibName $basename$version
92         }
93
94         lappend dirs [file join [file dirname [info library]] \
95                 $basename$version]
96
97         set parentDir [file dirname [file dirname [info nameofexecutable]]]
98         set grandParentDir [file dirname $parentDir]
99         lappend dirs [file join $parentDir lib $basename$version]
100         lappend dirs [file join $grandParentDir lib $basename$version]
101         lappend dirs [file join $parentDir library]
102         lappend dirs [file join $grandParentDir library]
103         if {![regexp {.*[ab][0-9]*} $patch ver]} {
104             set ver $version
105         }
106         lappend dirs [file join $grandParentDir $basename$ver library]
107         lappend dirs [file join [file dirname $grandParentDir] $basename$ver library]
108     }
109
110     foreach i $dirs {
111         set the_library $i
112         set file [file join $i $initScript]
113
114         # source everything when in a safe interpreter because
115         # we have a source command, but no file exists command
116
117         if {[interp issafe] || [file exists $file]} {
118             if {$debug_startup} {
119             
120               if {![catch {uplevel \#0 debugger_eval [list [list source $file]]} msg]} {
121                     return
122                 } else {
123                     append errors "$file: $msg\n$errorInfo\n"
124                 }
125             } else {
126                 if {![catch {uplevel \#0 [list source $file]} msg]} {
127                     return
128                 } else {
129                     append errors "$file: $msg\n$errorInfo\n"
130                 }
131             }
132         }
133     }
134     set msg "Can't find a usable $initScript in the following directories: \n"
135     append msg "    $dirs\n\n"
136     append msg "$errors\n\n"
137     append msg "This probably means that $basename wasn't installed properly.\n"
138     error $msg
139 }
140
141 # ----------------------------------------------------------------------
142 # auto_mkindex
143 # ----------------------------------------------------------------------
144 # The following procedures are used to generate the tclIndex file
145 # from Tcl source files.  They use a special safe interpreter to
146 # parse Tcl source files, writing out index entries as "proc"
147 # commands are encountered.  This implementation won't work in a
148 # safe interpreter, since a safe interpreter can't create the
149 # special parser and mess with its commands.  
150
151 if {[interp issafe]} {
152     return      ;# Stop sourcing the file here
153 }
154
155 # auto_mkindex --
156 # Regenerate a tclIndex file from Tcl source files.  Takes as argument
157 # the name of the directory in which the tclIndex file is to be placed,
158 # followed by any number of glob patterns to use in that directory to
159 # locate all of the relevant files.
160 #
161 # Arguments: 
162 # dir -         Name of the directory in which to create an index.
163 # args -        Any number of additional arguments giving the
164 #               names of files within dir.  If no additional
165 #               are given auto_mkindex will look for *.tcl.
166
167 proc auto_mkindex {dir args} {
168     global errorCode errorInfo
169
170     if {[interp issafe]} {
171         error "can't generate index within safe interpreter"
172     }
173
174     set oldDir [pwd]
175     cd $dir
176     set dir [pwd]
177
178     append index "# Tcl autoload index file, version 2.0\n"
179     append index "# This file is generated by the \"auto_mkindex\" command\n"
180     append index "# and sourced to set up indexing information for one or\n"
181     append index "# more commands.  Typically each line is a command that\n"
182     append index "# sets an element in the auto_index array, where the\n"
183     append index "# element name is the name of a command and the value is\n"
184     append index "# a script that loads the command.\n\n"
185     if {$args == ""} {
186         set args *.tcl
187     }
188
189     auto_mkindex_parser::init
190     foreach file [eval glob $args] {
191         if {[catch {auto_mkindex_parser::mkindex $file} msg] == 0} {
192             append index $msg
193         } else {
194             set code $errorCode
195             set info $errorInfo
196             cd $oldDir
197             error $msg $info $code
198         }
199     }
200     auto_mkindex_parser::cleanup
201
202     set fid [open "tclIndex" w]
203     puts -nonewline $fid $index
204     close $fid
205     cd $oldDir
206 }
207
208 # Original version of auto_mkindex that just searches the source
209 # code for "proc" at the beginning of the line.
210
211 proc auto_mkindex_old {dir args} {
212     global errorCode errorInfo
213     set oldDir [pwd]
214     cd $dir
215     set dir [pwd]
216     append index "# Tcl autoload index file, version 2.0\n"
217     append index "# This file is generated by the \"auto_mkindex\" command\n"
218     append index "# and sourced to set up indexing information for one or\n"
219     append index "# more commands.  Typically each line is a command that\n"
220     append index "# sets an element in the auto_index array, where the\n"
221     append index "# element name is the name of a command and the value is\n"
222     append index "# a script that loads the command.\n\n"
223     if {[string equal $args ""]} {
224         set args *.tcl
225     }
226     foreach file [eval glob $args] {
227         set f ""
228         set error [catch {
229             set f [open $file]
230             while {[gets $f line] >= 0} {
231                 if {[regexp {^proc[     ]+([^   ]*)} $line match procName]} {
232                     set procName [lindex [auto_qualify $procName "::"] 0]
233                     append index "set [list auto_index($procName)]"
234                     append index " \[list source \[file join \$dir [list $file]\]\]\n"
235                 }
236             }
237             close $f
238         } msg]
239         if {$error} {
240             set code $errorCode
241             set info $errorInfo
242             catch {close $f}
243             cd $oldDir
244             error $msg $info $code
245         }
246     }
247     set f ""
248     set error [catch {
249         set f [open tclIndex w]
250         puts -nonewline $f $index
251         close $f
252         cd $oldDir
253     } msg]
254     if {$error} {
255         set code $errorCode
256         set info $errorInfo
257         catch {close $f}
258         cd $oldDir
259         error $msg $info $code
260     }
261 }
262
263 # Create a safe interpreter that can be used to parse Tcl source files
264 # generate a tclIndex file for autoloading.  This interp contains
265 # commands for things that need index entries.  Each time a command
266 # is executed, it writes an entry out to the index file.
267
268 namespace eval auto_mkindex_parser {
269     variable parser ""          ;# parser used to build index
270     variable index ""           ;# maintains index as it is built
271     variable scriptFile ""      ;# name of file being processed
272     variable contextStack ""    ;# stack of namespace scopes
273     variable imports ""         ;# keeps track of all imported cmds
274     variable initCommands ""    ;# list of commands that create aliases
275
276     proc init {} {
277         variable parser
278         variable initCommands
279
280         if {![interp issafe]} {
281             set parser [interp create -safe]
282             $parser hide info
283             $parser hide rename
284             $parser hide proc
285             $parser hide namespace
286             $parser hide eval
287             $parser hide puts
288             $parser invokehidden namespace delete ::
289             $parser invokehidden proc unknown {args} {}
290
291             # We'll need access to the "namespace" command within the
292             # interp.  Put it back, but move it out of the way.
293
294             $parser expose namespace
295             $parser invokehidden rename namespace _%@namespace
296             $parser expose eval
297             $parser invokehidden rename eval _%@eval
298
299             # Install all the registered psuedo-command implementations
300
301             foreach cmd $initCommands {
302                 eval $cmd
303             }
304         }
305     }
306     proc cleanup {} {
307         variable parser
308         interp delete $parser
309         unset parser
310     }
311 }
312
313 # auto_mkindex_parser::mkindex --
314 #
315 # Used by the "auto_mkindex" command to create a "tclIndex" file for
316 # the given Tcl source file.  Executes the commands in the file, and
317 # handles things like the "proc" command by adding an entry for the
318 # index file.  Returns a string that represents the index file.
319 #
320 # Arguments: 
321 #       file    Name of Tcl source file to be indexed.
322
323 proc auto_mkindex_parser::mkindex {file} {
324     variable parser
325     variable index
326     variable scriptFile
327     variable contextStack
328     variable imports
329
330     set scriptFile $file
331
332     set fid [open $file]
333     set contents [read $fid]
334     close $fid
335
336     # There is one problem with sourcing files into the safe
337     # interpreter:  references like "$x" will fail since code is not
338     # really being executed and variables do not really exist.
339     # To avoid this, we replace all $ with \0 (literally, the null char)
340     # later, when getting proc names we will have to reverse this replacement,
341     # in case there were any $ in the proc name.  This will cause a problem
342     # if somebody actually tries to have a \0 in their proc name.  Too bad
343     # for them.
344     regsub -all {\$} $contents "\0" contents
345     
346     set index ""
347     set contextStack ""
348     set imports ""
349
350     $parser eval $contents
351
352     foreach name $imports {
353         catch {$parser eval [list _%@namespace forget $name]}
354     }
355     return $index
356 }
357
358 # auto_mkindex_parser::hook command
359 #
360 # Registers a Tcl command to evaluate when initializing the
361 # slave interpreter used by the mkindex parser.
362 # The command is evaluated in the master interpreter, and can
363 # use the variable auto_mkindex_parser::parser to get to the slave
364
365 proc auto_mkindex_parser::hook {cmd} {
366     variable initCommands
367
368     lappend initCommands $cmd
369 }
370
371 # auto_mkindex_parser::slavehook command
372 #
373 # Registers a Tcl command to evaluate when initializing the
374 # slave interpreter used by the mkindex parser.
375 # The command is evaluated in the slave interpreter.
376
377 proc auto_mkindex_parser::slavehook {cmd} {
378     variable initCommands
379
380     # The $parser variable is defined to be the name of the
381     # slave interpreter when this command is used later.
382
383     lappend initCommands "\$parser eval [list $cmd]"
384 }
385
386 # auto_mkindex_parser::command --
387 #
388 # Registers a new command with the "auto_mkindex_parser" interpreter
389 # that parses Tcl files.  These commands are fake versions of things
390 # like the "proc" command.  When you execute them, they simply write
391 # out an entry to a "tclIndex" file for auto-loading.
392 #
393 # This procedure allows extensions to register their own commands
394 # with the auto_mkindex facility.  For example, a package like
395 # [incr Tcl] might register a "class" command so that class definitions
396 # could be added to a "tclIndex" file for auto-loading.
397 #
398 # Arguments:
399 #       name    Name of command recognized in Tcl files.
400 #       arglist Argument list for command.
401 #       body    Implementation of command to handle indexing.
402
403 proc auto_mkindex_parser::command {name arglist body} {
404     hook [list auto_mkindex_parser::commandInit $name $arglist $body]
405 }
406
407 # auto_mkindex_parser::commandInit --
408 #
409 # This does the actual work set up by auto_mkindex_parser::command
410 # This is called when the interpreter used by the parser is created.
411 #
412 # Arguments:
413 #       name    Name of command recognized in Tcl files.
414 #       arglist Argument list for command.
415 #       body    Implementation of command to handle indexing.
416
417 proc auto_mkindex_parser::commandInit {name arglist body} {
418     variable parser
419
420     set ns [namespace qualifiers $name]
421     set tail [namespace tail $name]
422     if {[string equal $ns ""]} {
423         set fakeName "[namespace current]::_%@fake_$tail"
424     } else {
425         set fakeName "_%@fake_$name"
426         regsub -all {::} $fakeName "_" fakeName
427         set fakeName "[namespace current]::$fakeName"
428     }
429     proc $fakeName $arglist $body
430
431     # YUK!  Tcl won't let us alias fully qualified command names,
432     # so we can't handle names like "::itcl::class".  Instead,
433     # we have to build procs with the fully qualified names, and
434     # have the procs point to the aliases.
435
436     if {[regexp {::} $name]} {
437         set exportCmd [list _%@namespace export [namespace tail $name]]
438         $parser eval [list _%@namespace eval $ns $exportCmd]
439  
440         # The following proc definition does not work if you
441         # want to tolerate space or something else diabolical
442         # in the procedure name, (i.e., space in $alias)
443         # The following does not work:
444         #   "_%@eval {$alias} \$args"
445         # because $alias gets concat'ed to $args.
446         # The following does not work because $cmd is somehow undefined
447         #   "set cmd {$alias} \; _%@eval {\$cmd} \$args"
448         # A gold star to someone that can make test
449         # autoMkindex-3.3 work properly
450
451         set alias [namespace tail $fakeName]
452         $parser invokehidden proc $name {args} "_%@eval {$alias} \$args"
453         $parser alias $alias $fakeName
454     } else {
455         $parser alias $name $fakeName
456     }
457     return
458 }
459
460 # auto_mkindex_parser::fullname --
461 # Used by commands like "proc" within the auto_mkindex parser.
462 # Returns the qualified namespace name for the "name" argument.
463 # If the "name" does not start with "::", elements are added from
464 # the current namespace stack to produce a qualified name.  Then,
465 # the name is examined to see whether or not it should really be
466 # qualified.  If the name has more than the leading "::", it is
467 # returned as a fully qualified name.  Otherwise, it is returned
468 # as a simple name.  That way, the Tcl autoloader will recognize
469 # it properly.
470 #
471 # Arguments:
472 # name -                Name that is being added to index.
473
474 proc auto_mkindex_parser::fullname {name} {
475     variable contextStack
476
477     if {![string match ::* $name]} {
478         foreach ns $contextStack {
479             set name "${ns}::$name"
480             if {[string match ::* $name]} {
481                 break
482             }
483         }
484     }
485
486     if {[string equal [namespace qualifiers $name] ""]} {
487         set name [namespace tail $name]
488     } elseif {![string match ::* $name]} {
489         set name "::$name"
490     }
491     
492     # Earlier, mkindex replaced all $'s with \0.  Now, we have to reverse
493     # that replacement.
494     regsub -all "\0" $name "\$" name
495     return $name
496 }
497
498 # Register all of the procedures for the auto_mkindex parser that
499 # will build the "tclIndex" file.
500
501 # AUTO MKINDEX:  proc name arglist body
502 # Adds an entry to the auto index list for the given procedure name.
503
504 auto_mkindex_parser::command proc {name args} {
505     variable index
506     variable scriptFile
507     # Do some fancy reformatting on the "source" call to handle platform
508     # differences with respect to pathnames.  Use format just so that the
509     # command is a little easier to read (otherwise it'd be full of 
510     # backslashed dollar signs, etc.
511     append index [list set auto_index([fullname $name])] \
512             [format { [list source [file join $dir %s]]} \
513             [file split $scriptFile]] "\n"
514 }
515
516 # Conditionally add support for Tcl byte code files.  There are some
517 # tricky details here.  First, we need to get the tbcload library
518 # initialized in the current interpreter.  We cannot load tbcload into the
519 # slave until we have done so because it needs access to the tcl_patchLevel
520 # variable.  Second, because the package index file may defer loading the
521 # library until we invoke a command, we need to explicitly invoke auto_load
522 # to force it to be loaded.  This should be a noop if the package has
523 # already been loaded
524
525 auto_mkindex_parser::hook {
526     if {![catch {package require tbcload}]} {
527         if {[llength [info commands tbcload::bcproc]] == 0} {
528             auto_load tbcload::bcproc
529         }
530         load {} tbcload $auto_mkindex_parser::parser
531
532         # AUTO MKINDEX:  tbcload::bcproc name arglist body
533         # Adds an entry to the auto index list for the given pre-compiled
534         # procedure name.  
535
536         auto_mkindex_parser::commandInit tbcload::bcproc {name args} {
537             variable index
538             variable scriptFile
539             # Do some nice reformatting of the "source" call, to get around
540             # path differences on different platforms.  We use the format
541             # command just so that the code is a little easier to read.
542             append index [list set auto_index([fullname $name])] \
543                     [format { [list source [file join $dir %s]]} \
544                     [file split $scriptFile]] "\n"
545         }
546     }
547 }
548
549 # AUTO MKINDEX:  namespace eval name command ?arg arg...?
550 # Adds the namespace name onto the context stack and evaluates the
551 # associated body of commands.
552 #
553 # AUTO MKINDEX:  namespace import ?-force? pattern ?pattern...?
554 # Performs the "import" action in the parser interpreter.  This is
555 # important for any commands contained in a namespace that affect
556 # the index.  For example, a script may say "itcl::class ...",
557 # or it may import "itcl::*" and then say "class ...".  This
558 # procedure does the import operation, but keeps track of imported
559 # patterns so we can remove the imports later.
560
561 auto_mkindex_parser::command namespace {op args} {
562     switch -- $op {
563         eval {
564             variable parser
565             variable contextStack
566
567             set name [lindex $args 0]
568             set args [lrange $args 1 end]
569
570             set contextStack [linsert $contextStack 0 $name]
571             $parser eval [list _%@namespace eval $name] $args
572             set contextStack [lrange $contextStack 1 end]
573         }
574         import {
575             variable parser
576             variable imports
577             foreach pattern $args {
578                 if {[string compare $pattern "-force"]} {
579                     lappend imports $pattern
580                 }
581             }
582             catch {$parser eval "_%@namespace import $args"}
583         }
584     }
585 }
586
587 return