auto.tcl 21 KB

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