auto.tcl 20 KB

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