home *** CD-ROM | disk | FTP | other *** search
/ PC World 2002 May / PCWorld_2002-05_cd.bin / Software / TemaCD / activetcltk / ActiveTcl8.3.4.1-8.win32-ix86.exe / ActiveTcl8.3.4.1-win32-ix86 / lib / tcl8.3 / auto.tcl next >
Encoding:
Text File  |  2001-10-22  |  18.7 KB  |  570 lines

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