init.tcl 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771
  1. # init.tcl --
  2. #
  3. # Default system startup file for Tcl-based applications. Defines
  4. # "unknown" procedure and auto-load facilities.
  5. #
  6. # Copyright © 1991-1993 The Regents of the University of California.
  7. # Copyright © 1994-1996 Sun Microsystems, Inc.
  8. # Copyright © 1998-1999 Scriptics Corporation.
  9. # Copyright © 2004 Kevin B. Kenny.
  10. # Copyright © 2018 Sean Woods
  11. #
  12. # All rights reserved.
  13. #
  14. # See the file "license.terms" for information on usage and redistribution
  15. # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  16. #
  17. package require -exact tcl 9.0.1
  18. # Compute the auto path to use in this interpreter.
  19. # The values on the path come from several locations:
  20. #
  21. # The environment variable TCLLIBPATH
  22. #
  23. # tcl_library, which is the directory containing this init.tcl script.
  24. # [tclInit] (Tcl_Init()) searches around for the directory containing this
  25. # init.tcl and defines tcl_library to that location before sourcing it.
  26. #
  27. # The parent directory of tcl_library. Adding the parent
  28. # means that packages in peer directories will be found automatically.
  29. #
  30. # Also add the directory ../lib relative to the directory where the
  31. # executable is located. This is meant to find binary packages for the
  32. # same architecture as the current executable.
  33. #
  34. # tcl_pkgPath, which is set by the platform-specific initialization routines
  35. # On UNIX it is compiled in
  36. # On Windows, it is not used
  37. #
  38. # (Ticket 41c9857bdd) In a safe interpreter, this file does not set
  39. # ::auto_path (other than to {} if it is undefined). The caller, typically
  40. # a Safe Base command, is responsible for setting ::auto_path.
  41. if {![info exists auto_path]} {
  42. if {[info exists env(TCLLIBPATH)] && (![interp issafe])} {
  43. set auto_path [apply {{} {
  44. lmap path $::env(TCLLIBPATH) {
  45. # Paths relative to unresolvable home dirs are ignored
  46. if {[catch {file tildeexpand $path} expanded_path]} {
  47. continue
  48. }
  49. set expanded_path
  50. }
  51. }}]
  52. } else {
  53. set auto_path ""
  54. }
  55. }
  56. namespace eval tcl {
  57. if {![interp issafe]} {
  58. variable Dir
  59. foreach Dir [list $::tcl_library [file dirname $::tcl_library]] {
  60. if {$Dir ni $::auto_path} {
  61. lappend ::auto_path $Dir
  62. }
  63. }
  64. set Dir [file join [file dirname [file dirname \
  65. [info nameofexecutable]]] lib]
  66. if {$Dir ni $::auto_path} {
  67. lappend ::auto_path $Dir
  68. }
  69. if {[info exists ::tcl_pkgPath]} { catch {
  70. foreach Dir $::tcl_pkgPath {
  71. if {$Dir ni $::auto_path} {
  72. lappend ::auto_path $Dir
  73. }
  74. }
  75. }}
  76. variable Path [encoding dirs]
  77. set Dir [file join $::tcl_library encoding]
  78. if {$Dir ni $Path} {
  79. lappend Path $Dir
  80. encoding dirs $Path
  81. }
  82. unset Dir Path
  83. }
  84. }
  85. namespace eval tcl::Pkg {}
  86. # Setup the unknown package handler
  87. if {[interp issafe]} {
  88. package unknown {::tcl::tm::UnknownHandler ::tclPkgUnknown}
  89. } else {
  90. # Set up search for Tcl Modules (TIP #189).
  91. # and setup platform specific unknown package handlers
  92. if {$tcl_platform(os) eq "Darwin"
  93. && $tcl_platform(platform) eq "unix"} {
  94. package unknown {::tcl::tm::UnknownHandler \
  95. {::tcl::MacOSXPkgUnknown ::tclPkgUnknown}}
  96. } else {
  97. package unknown {::tcl::tm::UnknownHandler ::tclPkgUnknown}
  98. }
  99. # Set up the 'clock' ensemble
  100. apply {{} {
  101. set cmdmap [dict create]
  102. foreach cmd {add clicks format microseconds milliseconds scan seconds} {
  103. dict set cmdmap $cmd ::tcl::clock::$cmd
  104. }
  105. namespace inscope ::tcl::clock [list namespace ensemble create -command \
  106. ::clock -map $cmdmap]
  107. ::tcl::unsupported::clock::configure -init-complete
  108. }}
  109. }
  110. # Conditionalize for presence of exec.
  111. if {[namespace which -command exec] eq ""} {
  112. # Some machines do not have exec. Also, on all
  113. # platforms, safe interpreters do not have exec.
  114. set auto_noexec 1
  115. }
  116. # Define a log command (which can be overwritten to log errors
  117. # differently, specially when stderr is not available)
  118. if {[namespace which -command tclLog] eq ""} {
  119. proc tclLog {string} {
  120. catch {puts stderr $string}
  121. }
  122. }
  123. # unknown --
  124. # This procedure is called when a Tcl command is invoked that doesn't
  125. # exist in the interpreter. It takes the following steps to make the
  126. # command available:
  127. #
  128. # 1. See if the autoload facility can locate the command in a
  129. # Tcl script file. If so, load it and execute it.
  130. # 2. If the command was invoked interactively at top-level:
  131. # (a) see if the command exists as an executable UNIX program.
  132. # If so, "exec" the command.
  133. # (b) see if the command requests csh-like history substitution
  134. # in one of the common forms !!, !<number>, or ^old^new. If
  135. # so, emulate csh's history substitution.
  136. # (c) see if the command is a unique abbreviation for another
  137. # command. If so, invoke the command.
  138. #
  139. # Arguments:
  140. # args - A list whose elements are the words of the original
  141. # command, including the command name.
  142. proc unknown args {
  143. variable ::tcl::UnknownPending
  144. global auto_noexec auto_noload env tcl_interactive errorInfo errorCode
  145. if {[info exists errorInfo]} {
  146. set savedErrorInfo $errorInfo
  147. }
  148. if {[info exists errorCode]} {
  149. set savedErrorCode $errorCode
  150. }
  151. set name [lindex $args 0]
  152. if {![info exists auto_noload]} {
  153. #
  154. # Make sure we're not trying to load the same proc twice.
  155. #
  156. if {[info exists UnknownPending($name)]} {
  157. return -code error "self-referential recursion\
  158. in \"unknown\" for command \"$name\""
  159. }
  160. set UnknownPending($name) pending
  161. set ret [catch {
  162. auto_load $name [uplevel 1 {::namespace current}]
  163. } msg opts]
  164. unset UnknownPending($name)
  165. if {$ret != 0} {
  166. dict append opts -errorinfo "\n (autoloading \"$name\")"
  167. return -options $opts $msg
  168. }
  169. if {![array size UnknownPending]} {
  170. unset UnknownPending
  171. }
  172. if {$msg} {
  173. if {[info exists savedErrorCode]} {
  174. set ::errorCode $savedErrorCode
  175. } else {
  176. unset -nocomplain ::errorCode
  177. }
  178. if {[info exists savedErrorInfo]} {
  179. set errorInfo $savedErrorInfo
  180. } else {
  181. unset -nocomplain errorInfo
  182. }
  183. set code [catch {uplevel 1 $args} msg opts]
  184. if {$code == 1} {
  185. #
  186. # Compute stack trace contribution from the [uplevel].
  187. # Note the dependence on how Tcl_AddErrorInfo, etc.
  188. # construct the stack trace.
  189. #
  190. set errInfo [dict get $opts -errorinfo]
  191. set errCode [dict get $opts -errorcode]
  192. set cinfo $args
  193. if {[string length [encoding convertto utf-8 $cinfo]] > 150} {
  194. set cinfo [string range $cinfo 0 150]
  195. while {[string length [encoding convertto utf-8 $cinfo]] > 150} {
  196. set cinfo [string range $cinfo 0 end-1]
  197. }
  198. append cinfo ...
  199. }
  200. set tail "\n (\"uplevel\" body line 1)\n invoked\
  201. from within\n\"uplevel 1 \$args\""
  202. set expect "$msg\n while executing\n\"$cinfo\"$tail"
  203. if {$errInfo eq $expect} {
  204. #
  205. # The stack has only the eval from the expanded command
  206. # Do not generate any stack trace here.
  207. #
  208. dict unset opts -errorinfo
  209. dict incr opts -level
  210. return -options $opts $msg
  211. }
  212. #
  213. # Stack trace is nested, trim off just the contribution
  214. # from the extra "eval" of $args due to the "catch" above.
  215. #
  216. set last [string last $tail $errInfo]
  217. if {$last + [string length $tail] != [string length $errInfo]} {
  218. # Very likely cannot happen
  219. return -options $opts $msg
  220. }
  221. set errInfo [string range $errInfo 0 $last-1]
  222. set tail "\"$cinfo\""
  223. set last [string last $tail $errInfo]
  224. if {$last < 0 || $last + [string length $tail] != [string length $errInfo]} {
  225. return -code error -errorcode $errCode \
  226. -errorinfo $errInfo $msg
  227. }
  228. set errInfo [string range $errInfo 0 $last-1]
  229. set tail "\n invoked from within\n"
  230. set last [string last $tail $errInfo]
  231. if {$last + [string length $tail] == [string length $errInfo]} {
  232. return -code error -errorcode $errCode \
  233. -errorinfo [string range $errInfo 0 $last-1] $msg
  234. }
  235. set tail "\n while executing\n"
  236. set last [string last $tail $errInfo]
  237. if {$last + [string length $tail] == [string length $errInfo]} {
  238. return -code error -errorcode $errCode \
  239. -errorinfo [string range $errInfo 0 $last-1] $msg
  240. }
  241. return -options $opts $msg
  242. } else {
  243. dict incr opts -level
  244. return -options $opts $msg
  245. }
  246. }
  247. }
  248. if {([info level] == 1) && ([info script] eq "")
  249. && [info exists tcl_interactive] && $tcl_interactive} {
  250. if {![info exists auto_noexec]} {
  251. set new [auto_execok $name]
  252. if {$new ne ""} {
  253. set redir ""
  254. if {[namespace which -command console] eq ""} {
  255. set redir ">&@stdout <@stdin"
  256. }
  257. uplevel 1 [list ::catch \
  258. [concat exec $redir $new [lrange $args 1 end]] \
  259. ::tcl::UnknownResult ::tcl::UnknownOptions]
  260. dict incr ::tcl::UnknownOptions -level
  261. return -options $::tcl::UnknownOptions $::tcl::UnknownResult
  262. }
  263. }
  264. if {$name eq "!!"} {
  265. set newcmd [history event]
  266. } elseif {[regexp {^!(.+)$} $name -> event]} {
  267. set newcmd [history event $event]
  268. } elseif {[regexp {^\^([^^]*)\^([^^]*)\^?$} $name -> old new]} {
  269. set newcmd [history event -1]
  270. catch {regsub -all -- $old $newcmd $new newcmd}
  271. }
  272. if {[info exists newcmd]} {
  273. tclLog $newcmd
  274. history change $newcmd 0
  275. uplevel 1 [list ::catch $newcmd \
  276. ::tcl::UnknownResult ::tcl::UnknownOptions]
  277. dict incr ::tcl::UnknownOptions -level
  278. return -options $::tcl::UnknownOptions $::tcl::UnknownResult
  279. }
  280. set ret [catch [list uplevel 1 [list info commands $name*]] candidates]
  281. if {$name eq "::"} {
  282. set name ""
  283. }
  284. if {$ret != 0} {
  285. dict append opts -errorinfo \
  286. "\n (expanding command prefix \"$name\" in unknown)"
  287. return -options $opts $candidates
  288. }
  289. # Filter out bogus matches when $name contained
  290. # a glob-special char [Bug 946952]
  291. if {$name eq ""} {
  292. # Handle empty $name separately due to strangeness
  293. # in [string first] (See RFE 1243354)
  294. set cmds $candidates
  295. } else {
  296. set cmds [list]
  297. foreach x $candidates {
  298. if {[string first $name $x] == 0} {
  299. lappend cmds $x
  300. }
  301. }
  302. }
  303. if {[llength $cmds] == 1} {
  304. uplevel 1 [list ::catch [lreplace $args 0 0 [lindex $cmds 0]] \
  305. ::tcl::UnknownResult ::tcl::UnknownOptions]
  306. dict incr ::tcl::UnknownOptions -level
  307. return -options $::tcl::UnknownOptions $::tcl::UnknownResult
  308. }
  309. if {[llength $cmds]} {
  310. return -code error "ambiguous command name \"$name\": [lsort $cmds]"
  311. }
  312. }
  313. return -code error -errorcode [list TCL LOOKUP COMMAND $name] \
  314. "invalid command name \"$name\""
  315. }
  316. # auto_load --
  317. # Checks a collection of library directories to see if a procedure
  318. # is defined in one of them. If so, it sources the appropriate
  319. # library file to create the procedure. Returns 1 if it successfully
  320. # loaded the procedure, 0 otherwise.
  321. #
  322. # Arguments:
  323. # cmd - Name of the command to find and load.
  324. # namespace (optional) The namespace where the command is being used - must be
  325. # a canonical namespace as returned [namespace current]
  326. # for instance. If not given, namespace current is used.
  327. proc auto_load {cmd {namespace {}}} {
  328. global auto_index auto_path
  329. # qualify names:
  330. if {$namespace eq ""} {
  331. set namespace [uplevel 1 [list ::namespace current]]
  332. }
  333. set nameList [auto_qualify $cmd $namespace]
  334. # workaround non canonical auto_index entries that might be around
  335. # from older auto_mkindex versions
  336. if {$cmd ni $nameList} {lappend nameList $cmd}
  337. # try to load (and create sub-cmd handler "_sub_load_cmd" for further usage):
  338. foreach name $nameList [set _sub_load_cmd {
  339. # via auto_index:
  340. if {[info exists auto_index($name)]} {
  341. namespace inscope :: $auto_index($name)
  342. # There's a couple of ways to look for a command of a given
  343. # name. One is to use
  344. # info commands $name
  345. # Unfortunately, if the name has glob-magic chars in it like *
  346. # or [], it may not match. For our purposes here, a better
  347. # route is to use
  348. # namespace which -command $name
  349. if {[namespace which -command $name] ne ""} {
  350. return 1
  351. }
  352. }
  353. }]
  354. # load auto_index if possible:
  355. if {![info exists auto_path]} {
  356. return 0
  357. }
  358. if {![auto_load_index]} {
  359. return 0
  360. }
  361. # try again (something new could be loaded):
  362. foreach name $nameList $_sub_load_cmd
  363. return 0
  364. }
  365. # ::tcl::Pkg::source --
  366. # This procedure provides an alternative "source" command, which doesn't
  367. # register the file for the "package files" command. Safe interpreters
  368. # don't have to do anything special.
  369. #
  370. # Arguments:
  371. # filename
  372. proc ::tcl::Pkg::source {filename} {
  373. if {[interp issafe]} {
  374. uplevel 1 [list ::source $filename]
  375. } else {
  376. uplevel 1 [list ::source -nopkg $filename]
  377. }
  378. }
  379. # auto_load_index --
  380. # Loads the contents of tclIndex files on the auto_path directory
  381. # list. This is usually invoked within auto_load to load the index
  382. # of available commands. Returns 1 if the index is loaded, and 0 if
  383. # the index is already loaded and up to date.
  384. #
  385. # Arguments:
  386. # None.
  387. proc auto_load_index {} {
  388. variable ::tcl::auto_oldpath
  389. global auto_index auto_path
  390. if {[info exists auto_oldpath] && ($auto_oldpath eq $auto_path)} {
  391. return 0
  392. }
  393. set auto_oldpath $auto_path
  394. # Check if we are a safe interpreter. In that case, we support only
  395. # newer format tclIndex files.
  396. set issafe [interp issafe]
  397. for {set i [expr {[llength $auto_path] - 1}]} {$i >= 0} {incr i -1} {
  398. set dir [lindex $auto_path $i]
  399. set f ""
  400. if {$issafe} {
  401. catch {source [file join $dir tclIndex]}
  402. } elseif {[catch {set f [open [file join $dir tclIndex]]}]} {
  403. continue
  404. } else {
  405. set error [catch {
  406. fconfigure $f -encoding utf-8 -eofchar \x1A
  407. set id [gets $f]
  408. if {$id eq "# Tcl autoload index file, version 2.0"} {
  409. eval [read $f]
  410. } elseif {$id eq "# Tcl autoload index file: each line identifies a Tcl"} {
  411. while {[gets $f line] >= 0} {
  412. if {([string index $line 0] eq "#") \
  413. || ([llength $line] != 2)} {
  414. continue
  415. }
  416. set name [lindex $line 0]
  417. set auto_index($name) \
  418. "::tcl::Pkg::source [file join $dir [lindex $line 1]]"
  419. }
  420. } else {
  421. error "[file join $dir tclIndex] isn't a proper Tcl index file"
  422. }
  423. } msg opts]
  424. if {$f ne ""} {
  425. close $f
  426. }
  427. if {$error} {
  428. return -options $opts $msg
  429. }
  430. }
  431. }
  432. return 1
  433. }
  434. # auto_qualify --
  435. #
  436. # Compute a fully qualified names list for use in the auto_index array.
  437. # For historical reasons, commands in the global namespace do not have leading
  438. # :: in the index key. The list has two elements when the command name is
  439. # relative (no leading ::) and the namespace is not the global one. Otherwise
  440. # only one name is returned (and searched in the auto_index).
  441. #
  442. # Arguments -
  443. # cmd The command name. Can be any name accepted for command
  444. # invocations (Like "foo::::bar").
  445. # namespace The namespace where the command is being used - must be
  446. # a canonical namespace as returned by [namespace current]
  447. # for instance.
  448. proc auto_qualify {cmd namespace} {
  449. # count separators and clean them up
  450. # (making sure that foo:::::bar will be treated as foo::bar)
  451. set n [regsub -all {::+} $cmd :: cmd]
  452. # Ignore namespace if the name starts with ::
  453. # Handle special case of only leading ::
  454. # Before each return case we give an example of which category it is
  455. # with the following form :
  456. # (inputCmd, inputNameSpace) -> output
  457. if {[string match ::* $cmd]} {
  458. if {$n > 1} {
  459. # (::foo::bar , *) -> ::foo::bar
  460. return [list $cmd]
  461. } else {
  462. # (::global , *) -> global
  463. return [list [string range $cmd 2 end]]
  464. }
  465. }
  466. # Potentially returning 2 elements to try :
  467. # (if the current namespace is not the global one)
  468. if {$n == 0} {
  469. if {$namespace eq "::"} {
  470. # (nocolons , ::) -> nocolons
  471. return [list $cmd]
  472. } else {
  473. # (nocolons , ::sub) -> ::sub::nocolons nocolons
  474. return [list ${namespace}::$cmd $cmd]
  475. }
  476. } elseif {$namespace eq "::"} {
  477. # (foo::bar , ::) -> ::foo::bar
  478. return [list ::$cmd]
  479. } else {
  480. # (foo::bar , ::sub) -> ::sub::foo::bar ::foo::bar
  481. return [list ${namespace}::$cmd ::$cmd]
  482. }
  483. }
  484. # auto_import --
  485. #
  486. # Invoked during "namespace import" to make see if the imported commands
  487. # reside in an autoloaded library. If so, the commands are loaded so
  488. # that they will be available for the import links. If not, then this
  489. # procedure does nothing.
  490. #
  491. # Arguments -
  492. # pattern The pattern of commands being imported (like "foo::*")
  493. # a canonical namespace as returned by [namespace current]
  494. proc auto_import {pattern} {
  495. global auto_index
  496. # If no namespace is specified, this will be an error case
  497. if {![string match *::* $pattern]} {
  498. return
  499. }
  500. set ns [uplevel 1 [list ::namespace current]]
  501. set patternList [auto_qualify $pattern $ns]
  502. auto_load_index
  503. foreach pattern $patternList {
  504. foreach name [array names auto_index $pattern] {
  505. if {([namespace which -command $name] eq "")
  506. && ([namespace qualifiers $pattern] eq [namespace qualifiers $name])} {
  507. namespace inscope :: $auto_index($name)
  508. }
  509. }
  510. }
  511. }
  512. # auto_execok --
  513. #
  514. # Returns string that indicates name of program to execute if
  515. # name corresponds to a shell builtin or an executable in the
  516. # Windows search path, or "" otherwise. Builds an associative
  517. # array auto_execs that caches information about previous checks,
  518. # for speed.
  519. #
  520. # Arguments:
  521. # name - Name of a command.
  522. if {$tcl_platform(platform) eq "windows"} {
  523. # Windows version.
  524. #
  525. # Note that file executable doesn't work under Windows, so we have to
  526. # look for files with .exe, .com, or .bat extensions. Also, the path
  527. # may be in the Path or PATH environment variables, and path
  528. # components are separated with semicolons, not colons as under Unix.
  529. #
  530. proc auto_execok name {
  531. global auto_execs env tcl_platform
  532. if {[info exists auto_execs($name)]} {
  533. return $auto_execs($name)
  534. }
  535. set auto_execs($name) ""
  536. set shellBuiltins [list assoc cls copy date del dir echo erase exit ftype \
  537. md mkdir mklink move rd ren rename rmdir start time type ver vol]
  538. if {[info exists env(PATHEXT)]} {
  539. # Add an initial ; to have the {} extension check first.
  540. set execExtensions [split ";$env(PATHEXT)" ";"]
  541. } else {
  542. set execExtensions [list {} .com .exe .bat .cmd]
  543. }
  544. if {[string tolower $name] in $shellBuiltins} {
  545. # When this is command.com for some reason on Win2K, Tcl won't
  546. # exec it unless the case is right, which this corrects. COMSPEC
  547. # may not point to a real file, so do the check.
  548. set cmd $env(COMSPEC)
  549. if {[file exists $cmd]} {
  550. set cmd [file attributes $cmd -shortname]
  551. }
  552. return [set auto_execs($name) [list $cmd /c $name]]
  553. }
  554. if {[llength [file split $name]] != 1} {
  555. foreach ext $execExtensions {
  556. set file ${name}${ext}
  557. if {[file exists $file] && ![file isdirectory $file]} {
  558. return [set auto_execs($name) [list $file]]
  559. }
  560. }
  561. return ""
  562. }
  563. set path "[file dirname [info nameofexecutable]];.;"
  564. if {[info exists env(SystemRoot)]} {
  565. set windir $env(SystemRoot)
  566. } elseif {[info exists env(WINDIR)]} {
  567. set windir $env(WINDIR)
  568. }
  569. if {[info exists windir]} {
  570. append path "$windir/system32;$windir/system;$windir;"
  571. }
  572. foreach var {PATH Path path} {
  573. if {[info exists env($var)]} {
  574. append path ";$env($var)"
  575. }
  576. }
  577. foreach ext $execExtensions {
  578. unset -nocomplain checked
  579. foreach dir [split $path {;}] {
  580. # Skip already checked directories
  581. if {[info exists checked($dir)] || ($dir eq "")} {
  582. continue
  583. }
  584. set checked($dir) {}
  585. set file [file join $dir ${name}${ext}]
  586. if {[file exists $file] && ![file isdirectory $file]} {
  587. return [set auto_execs($name) [list $file]]
  588. }
  589. }
  590. }
  591. return ""
  592. }
  593. } else {
  594. # Unix version.
  595. #
  596. proc auto_execok name {
  597. global auto_execs env
  598. if {[info exists auto_execs($name)]} {
  599. return $auto_execs($name)
  600. }
  601. set auto_execs($name) ""
  602. if {[llength [file split $name]] != 1} {
  603. if {[file executable $name] && ![file isdirectory $name]} {
  604. set auto_execs($name) [list $name]
  605. }
  606. return $auto_execs($name)
  607. }
  608. foreach dir [split $env(PATH) :] {
  609. if {$dir eq ""} {
  610. set dir .
  611. }
  612. set file [file join $dir $name]
  613. if {[file executable $file] && ![file isdirectory $file]} {
  614. set auto_execs($name) [list $file]
  615. return $auto_execs($name)
  616. }
  617. }
  618. return ""
  619. }
  620. }
  621. # ::tcl::CopyDirectory --
  622. #
  623. # This procedure is called by Tcl's core when attempts to call the
  624. # filesystem's copydirectory function fail. The semantics of the call
  625. # are that 'dest' does not yet exist, i.e. dest should become the exact
  626. # image of src. If dest does exist, we throw an error.
  627. #
  628. # Note that making changes to this procedure can change the results
  629. # of running Tcl's tests.
  630. #
  631. # Arguments:
  632. # action - "renaming" or "copying"
  633. # src - source directory
  634. # dest - destination directory
  635. proc tcl::CopyDirectory {action src dest} {
  636. set nsrc [file normalize $src]
  637. set ndest [file normalize $dest]
  638. if {$action eq "renaming"} {
  639. # Can't rename volumes. We could give a more precise
  640. # error message here, but that would break the test suite.
  641. if {$nsrc in [file volumes]} {
  642. return -code error "error $action \"$src\" to\
  643. \"$dest\": trying to rename a volume or move a directory\
  644. into itself"
  645. }
  646. }
  647. if {[file exists $dest]} {
  648. if {$nsrc eq $ndest} {
  649. return -code error "error $action \"$src\" to\
  650. \"$dest\": trying to rename a volume or move a directory\
  651. into itself"
  652. }
  653. if {$action eq "copying"} {
  654. # We used to throw an error here, but, looking more closely
  655. # at the core copy code in tclFCmd.c, if the destination
  656. # exists, then we should only call this function if -force
  657. # is true, which means we just want to over-write. So,
  658. # the following code is now commented out.
  659. #
  660. # return -code error "error $action \"$src\" to\
  661. # \"$dest\": file exists"
  662. } else {
  663. # Depending on the platform, and on the current
  664. # working directory, the directories '.', '..'
  665. # can be returned in various combinations. Anyway,
  666. # if any other file is returned, we must signal an error.
  667. set existing [glob -nocomplain -directory $dest * .*]
  668. lappend existing {*}[glob -nocomplain -directory $dest \
  669. -type hidden * .*]
  670. foreach s $existing {
  671. if {[file tail $s] ni {. ..}} {
  672. return -code error "error $action \"$src\" to\
  673. \"$dest\": file exists"
  674. }
  675. }
  676. }
  677. } else {
  678. if {[string first $nsrc $ndest] >= 0} {
  679. set srclen [expr {[llength [file split $nsrc]] - 1}]
  680. set ndest [lindex [file split $ndest] $srclen]
  681. if {$ndest eq [file tail $nsrc]} {
  682. return -code error "error $action \"$src\" to\
  683. \"$dest\": trying to rename a volume or move a directory\
  684. into itself"
  685. }
  686. }
  687. file mkdir $dest
  688. }
  689. # Have to be careful to capture both visible and hidden files.
  690. # We will also be more generous to the file system and not
  691. # assume the hidden and non-hidden lists are non-overlapping.
  692. #
  693. # On Unix 'hidden' files begin with '.'. On other platforms
  694. # or filesystems hidden files may have other interpretations.
  695. set filelist [concat [glob -nocomplain -directory $src *] \
  696. [glob -nocomplain -directory $src -types hidden *]]
  697. foreach s [lsort -unique $filelist] {
  698. if {[file tail $s] ni {. ..}} {
  699. file copy -force -- $s [file join $dest [file tail $s]]
  700. }
  701. }
  702. return
  703. }