auto.tcl 20 KB

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