auto.tcl 21 KB

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