tcl-import.tcl
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:24k
源码类别:

通讯编程

开发平台:

Visual C++

  1. #
  2. # Copyright (c) 1998 Regents of the University of California.
  3. # All rights reserved.
  4. #
  5. # Redistribution and use in source and binary forms, with or without
  6. # modification, are permitted provided that the following conditions
  7. # are met:
  8. # 1. Redistributions of source code must retain the above copyright
  9. #    notice, this list of conditions and the following disclaimer.
  10. # 2. Redistributions in binary form must reproduce the above copyright
  11. #    notice, this list of conditions and the following disclaimer in the
  12. #    documentation and/or other materials provided with the distribution.
  13. # 3. All advertising materials mentioning features or use of this software
  14. #    must display the following acknowledgement:
  15. #       This product includes software developed by the Computer Systems
  16. #       Engineering Group at Lawrence Berkeley Laboratory.
  17. # 4. Neither the name of the University nor of the Laboratory may be used
  18. #    to endorse or promote products derived from this software without
  19. #    specific prior written permission.
  20. #
  21. # THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22. # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24. # ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25. # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26. # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27. # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28. # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29. # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30. # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31. # SUCH DAMAGE.
  32. #
  33. # ------------
  34. #
  35. # Filename: new-tcl-import.tcl
  36. #   -- Created on Fri Jul 24 1998
  37. #   -- Author: Yatin Chawathe <yatin@cs.berkeley.edu>
  38. #
  39. # Description:
  40. #
  41. #
  42. #  @(#) $Header: /cvsroot/otcl-tclcl/tclcl/tcl-import.tcl,v 1.6 2002/07/12 00:25:46 tim1724 Exp $
  43. #
  44. #
  45. # Support for a simple, java-like import construct.
  46. #
  47. Class Import
  48. Import public init { } {
  49. $self next
  50. $self set use_http_cache_ 1
  51. }
  52. #
  53. # For the objects provided as <i>args</i>, source the files in
  54. # which their class & methods are defined.
  55. # After attempting to import all supplied items, if any were unimportable,
  56. # an error will be flagged with a detailed errormsg.
  57. #
  58. Import public import { args } {
  59. $self instvar import_dirs_ table_
  60. # initialize the import table only on demand
  61. if { ![info exists import_dirs_] } {
  62. $self init_table
  63. }
  64. # ensure that the TCLCL_IMPORT_DIRS env var hasn't changed since we
  65. # initialized the table
  66. $self consistency_check
  67. foreach item $args {
  68. if [info exists table_($item)] {
  69. set file_list $table_($item)
  70. # although it's poor programming practice,
  71. # an object can be defined in multiple files
  72. foreach file $table_($item) {
  73. if { [set msg [$self source_file $file]]!=""} {
  74. error "could not source $file for
  75. $item:n$msg"
  76. }
  77. }
  78. } else {
  79. # if the object is not in the table_,
  80. # try searching for default name (<object>.mash)
  81. # in each of the env(TCLCL_IMPORT_DIRS)
  82. set list {}
  83. foreach dir $import_dirs_ {
  84. lappend list [$self file join $dir 
  85. [$self class_to_file 
  86. $item].mash]
  87. }
  88. set imported 0
  89. foreach filename $list {
  90. if { [$self source_file $filename] == "" } {
  91. set imported 1
  92. break
  93. }
  94. }
  95. if { ! $imported } {
  96. error "don't know how to import $itemn    not
  97. mapped in: $import_dirs_
  98. n    and not found in default
  99. locations: $list"
  100. }
  101. }
  102. }
  103. }
  104. #
  105. # As long as the import procedure has not yet been invoked, the user is
  106. # free to override mappings that may be read from importTables.
  107. #
  108. Import public override_importTable_mapping { object file_list } {
  109. $self instvar overrideTable_ import_dirs_
  110. if { [info exists import_dirs_] } {
  111. puts stderr "warning: ignoring "override_importTable_mapping
  112. $object $file_list" n
  113. It is illegal to modify the internal table 
  114. after the first call to import."
  115. return
  116. }
  117. if { [info exists overrideTable_($object)] } {
  118. unset overrideTable_($object)
  119. }
  120. foreach file $file_list {
  121. set fname [$self condense_into_absolute_filename 
  122. [$self file join [pwd] $file]]
  123. lappend overrideTable_($object) $fname
  124. }
  125. }
  126. #
  127. # Wait to redefine the unknown proc until we are running in the interpreter,
  128. # because the original unknown proc is not defined until after this
  129. # tcl-object.tcl code is processed. (I believe the original unknown proc
  130. # is actually defined when Tcl_AppInit calls Tcl_Init to set up the
  131. # script library facility.)
  132. #
  133. Import proc.private redefine_unknown {} {
  134. #
  135. # If a proc/instproc is called on an unknown class, you'll wind
  136. # up here. If auto-importing is enabled, attempt to import the class.
  137. #
  138. rename unknown unknown.orig
  139. # Rather than redefining this procedure in tcl/library/init.tcl,
  140. # we can rename & augment it here for the mash interpreter.
  141. proc unknown { args } {
  142. # first try tcl's original unknown proc and return if
  143. # successful
  144. if { ![catch "eval {unknown.orig} $args" m] } { 
  145. return
  146. }
  147. # otherwise, if autoimporting is enabled,
  148. # if able to import an item by this name, do so and return
  149. # btw, if the stuff in catch quotes causes "unknown" to
  150. # get called, error = "too many nested calls to
  151. # Tcl_EvalObj (infinite loop?)"
  152. $self instvar autoimport_
  153. if { [info exists autoimport_] && $autoimport_ } {
  154.                         really_import [lindex $args 0]
  155. } else {
  156. # if not trying to import, puts original error msg
  157. error "$m" 
  158. }
  159. }
  160. # prevent this method from being called again
  161. Import proc.private redefine_unknown {} {}
  162. }
  163. #
  164. # As new objects are needed, the unknown proc will catch them and
  165. # import the files that define them and their methods.
  166. # As an intended side-effect, explicit imports will be ignored
  167. # until auto-import is disabled.
  168. #
  169. Import proc.public enable_autoimport {} {
  170. # XXX this should be done somewhere else
  171. import Class Object mashutils
  172. Import set autoimport_ 1
  173. $self redefine_unknown
  174. return
  175. }
  176. #
  177. Import proc.public disable_autoimport {} {
  178. Import set autoimport_ 0
  179. return
  180. }
  181. #
  182. # Auto-importing is disabled by default.
  183. # (And because dynamic loading using unknown yet, due to the fact the unknown
  184. #  isn't called when the -auperclass attribute is used in a Class defn.)
  185. #
  186. Import disable_autoimport 
  187. #
  188. # Read the environment variable, TCLCL_IMPORT_DIRS, and store the directories in a list instvar.
  189. # Afterwards, makes a call to the instproc that generates the table.
  190. #
  191. Import private init_table { } {
  192. $self instvar import_dirs_
  193. global env
  194. # If TCLCL_IMPORT_DIRS is not set before first time import proc
  195. # is called, it is set to '.'
  196. # Note that otherwise, '.' is not appended to TCLCL_IMPORT_DIRS.
  197. if { ![info exists env(TCLCL_IMPORT_DIRS)] } {
  198. set env(TCLCL_IMPORT_DIRS) .
  199. }
  200. set import_dirs_ ""
  201. foreach dir [$self smart_parse_env_var $env(TCLCL_IMPORT_DIRS)] {
  202. # If dir is relative, it is expanded to absolute.
  203. # Relative pathnames in TCLCL_IMPORT_DIRS will be considered
  204. # relative to '.'
  205. # which is the directory the mash interpreter was launched from
  206. # (unless the cd proc has been called since then)
  207. # So if mashlets are bing run from a browser, '.' starts out as
  208. # the directory from which the browser was launched.
  209. lappend import_dirs_ [$self condense_to_absolute_filename $dir]
  210. }
  211. # locate the actual import directories
  212. set dirs [$self find_import_dirs $import_dirs_]
  213. # the first time import is called, build a table of mappings from
  214. # objects to the file(s) they are defined in
  215. $self make_table $dirs
  216. }
  217. #
  218. # Build an internal table of mappings from objects to the file(s) they
  219. # are defined in.
  220. #
  221. Import private make_table { dirs } {
  222. foreach d $dirs {
  223. $self read_dir $d
  224. }
  225. $self incorporate_table_overrides
  226. }
  227. #
  228. # After the table_ has been generated, override existing mappings with
  229. # any user-defined mappings.
  230. #
  231. Import private incorporate_table_overrides {} {
  232. $self instvar overrideTable_ table_
  233. foreach object [array names overrideTable_] {
  234. set table_($object) $overrideTable_($object)
  235. }
  236. }
  237. #
  238. # Return a list of directories in which a readable importTable can be found.
  239. # For every path in TCLCL_IMPORT_DIRS, importLocation will be read, if
  240. # it exists, and the absolute pathnames of the importTables that it points
  241. # to will be appended to import_table_list_ .
  242. # For dirs in which a readable importLocation file is not found,
  243. # the absolute pathname of the importTable in that dir, if one exists,
  244. # will be appended instead.
  245. # Directories may be named using complete pathnames or pathnames relative
  246. # to CURRENTDIR.
  247. #
  248. Import private find_import_dirs { dirs } {
  249. # Generate a list of potential directories in which an importTable
  250. # may be found
  251. set list {}
  252. foreach dir $dirs {
  253. set importLocation [$self file join $dir importLocation]
  254. set r [$self file readable $importLocation]
  255. if [lindex $r 0] {
  256. set lines [$self read_file_into_list $importLocation]
  257. foreach line $lines {
  258. # append absolute filename on this line to
  259. # the list
  260. lappend list [$self 
  261. condense_to_absolute_filename 
  262. [$self file join $dir $line]]
  263. }
  264. if { [lindex $r 1] != {} } {
  265. # destroy the http token
  266. unset [lindex $r 1]
  267. }
  268. } else {
  269. lappend list $dir
  270. }
  271. }
  272. # prune the list down to the directories in which an importTable
  273. # is actually readable
  274. $self instvar last_modified_
  275. set dirs ""
  276. foreach d $list {
  277. set import_table [$self file join $d importTable]
  278. set last_modified_($import_table) -1
  279. set r [$self file readable $import_table]
  280. if [lindex $r 0] {
  281. lappend dirs $d
  282. if { [lindex $r 1] != {} } {
  283. # destroy the http token
  284. unset [lindex $r 1]
  285. }
  286. }
  287. }
  288. #if { [llength $dirs] > 0 } {
  289. # puts stderr "readable importTables found in: $dirs"
  290. #} else {
  291. # puts stderr "no readable importTables found"
  292. #}
  293. return $dirs
  294. }
  295. #
  296. # By reading the importTable in the provided directory <i>dir</i>,
  297. # continue to define the elements of the table_ array.  Each element of
  298. # this array is indexed using an object name and consists of a list of
  299. # files (using absolute pathnames) in which the the object and its
  300. # methods are defined.  Returns the table_ in list form.
  301. #
  302. Import private read_dir { dir } {
  303. $self instvar table_ classes_mapped_ last_modified_
  304. set importTableFile [$self condense_to_absolute_filename 
  305. [$self file join $dir importTable]]
  306. set last_modified_($importTableFile) -1
  307. # fetch the importTable and break it into a list of lines
  308. set lines [$self read_file_into_list $importTableFile]
  309. # for every line in the importTable, parse out the object and
  310. # the filename, adding the file to the appropriate list element
  311. # of the table_ array if it is not there already
  312. foreach line $lines {
  313. set index [lindex $line 0]
  314. #  use the absolute file name
  315. # (relative filenames are considered to be relative to
  316. # the directory containing the importTable)
  317. set fname [$self condense_to_absolute_filename 
  318. [$self file join $dir [lindex $line 1]]]
  319. set last_modified [string trim [lindex $line 2]]
  320. # if a mapping for this object was already read from
  321. # another importTable, ignore this one
  322. if [info exists classes_mapped_($index)] {
  323. continue
  324. }
  325. # if this object already has a mapping to this filename,
  326. # skip it
  327. if {[info exists table_($index)]} {
  328. if {-1!=[lsearch -exact $table_($index) $fname]} {
  329. continue
  330. }
  331. }
  332. lappend table_($index) $fname
  333. if { $last_modified!={} } {
  334. set last_modified_($fname) $last_modified
  335. }
  336. set this_mappings($index) 1
  337. }
  338. foreach index [array names this_mappings] {
  339. set classes_mapped_($index) 1
  340. }
  341. }
  342. # Provide the value of an env variable and a list of the ':' separated
  343. # items is returned.
  344. #
  345. # Since [split $env(TCLCL_IMPORT_DIRS) :]
  346. # doesn't work very well if some of the ':' separated items are
  347. # URLs (w/"http:"), you can use this proc to intelligently parse up
  348. # an env variable
  349. #
  350. Import private smart_parse_env_var { env_value } {
  351. set env $env_value
  352. while {[string length [set env [string trim $env ":"]]] != 0 } {
  353. if [regexp {^([^:]+)://([^:/]+)(:([0-9]+))?(/[^:]*)} 
  354. $env url protocol server x port trailingpath] {
  355. lappend list $url
  356. regsub {([^:]+)://([^:/]+)(:([0-9]+))?(/[^:]*)} 
  357. $env {} env
  358. } else {
  359. regexp {^[^:]+} $env dir
  360. lappend list $dir
  361. regsub {^[^:]+} $env {} env
  362. }
  363. }
  364. return $list
  365. }
  366. Import private consistency_check { } {
  367. global env
  368. $self instvar orig_val_
  369. if { ![info exists orig_val_] } {
  370. set orig_val_ $env(TCLCL_IMPORT_DIRS)
  371. }
  372. if { $env(TCLCL_IMPORT_DIRS) != $orig_val_ } {
  373. puts stderr "warning: ignoring modification to
  374. env(TCLCL_IMPORT_DIRS)nit is illegal to
  375. modify this after the first call to the
  376. import procedure."
  377. }
  378. }
  379. #
  380. # returns an empty string on success, or the error string on failure
  381. #
  382. Import private source_file { file } {
  383. set file_readable_result [$self file readable $file]
  384. set file_readable [lindex $file_readable_result 0]
  385. if { $file_readable } {
  386. set read_token [lindex $file_readable_result 1] 
  387. $self source $file $read_token
  388. if { $read_token!={} } { unset $read_token }
  389. return ""
  390. } else {
  391. return [lindex $file_readable_result 1]
  392. }
  393. }
  394. Import private source { file { read_token {} } } {
  395. $self instvar loaded_ uniq_num_
  396. if { ![info exists uniq_num_] } {
  397. set uniq_num_ 0
  398. }
  399. # make sure the filename has been expanded to an absolute path
  400. # with sym links followed and extraneous '.'s and '..'s removed
  401. # so that we are most likely to recognize repeated attempts to
  402. # source the same file
  403. set file [$self condense_to_absolute_filename $file]
  404. if [info exists loaded_($file)] {
  405. #puts stdout "$fileName previously sourced. 
  406. #Not re-sourcing it."
  407. return 
  408. }
  409. set loaded_($file) 1
  410. # redirects explicit calls to the source proc within fileName
  411. # to this source proc in order to ensure that files
  412. # are not repeatedly sourced
  413. incr uniq_num_
  414. uplevel #0 "rename source source.$uniq_num_"
  415. uplevel #0 "proc source {args} { $self source $args }"
  416. if [$self is_http_url $file] {
  417. set buffer [$self read_url $file $read_token]
  418. # catch errors so that errors in the file being sourced
  419. # do not cause this script to be exited prematurely
  420. # (btw, need {} around $buffer so variable substitutions
  421. # occur in the global scope and so blank lines at beginning
  422. # of buffer don't cause errors
  423. # XXX: the buffer must be sourced in the context where
  424. # "import" was called from!
  425. if [catch "uplevel #0 {$buffer}" errormsg] {
  426. global errorInfo
  427. error "error in $file: $errormsgn$errorInfonn"
  428. }
  429. } else {
  430. # catch errors so that errors in the file being sourced
  431. # do not cause this script to be exited prematurely
  432. # XXX: the buffer must be sourced in the context where
  433. # "import" was called from!
  434. if [catch "uplevel #0 source.orig $file" errormsg] {
  435. global errorInfo
  436. error "error in $file: $errormsgn$errorInfonn"
  437. }
  438. }
  439. # disabling the redirection to this alternate source proc
  440. uplevel #0 {rename source {}}
  441. uplevel #0 "rename source.$uniq_num_ source"
  442. incr uniq_num_ -1
  443. }
  444. #
  445. # dummy procedure for backward compatibility
  446. # Import is always enabled by default
  447. #
  448. Import private enable { args } {
  449. }
  450. #
  451. # Map an OTcl class name <i>c</i> to a http-able name
  452. #
  453. Import private class_to_file c {
  454. regsub -all -- "/" $c "-" filename
  455. return $filename
  456. }
  457. #
  458. # Return 1 if the <i>name</i> is a http URL and 0 otherwise.
  459. # An http URL is of the form http://<server and possibly port num>/
  460. # Note that the trailing slash is important.
  461. #
  462. Import private is_http_url { name } {
  463. if [regexp {([^:]+)://([^:/]+)(:([0-9]+))?(/.*)} $name url protocol 
  464. server x port trailingpath] {
  465. if { ![info exists protocol] } {
  466. return 0
  467. } else {
  468. return [regexp -nocase {http} $protocol]
  469. }
  470. } else {
  471. return 0
  472. }
  473. }
  474. #
  475. # Returns a buffer read from the provided http <i>url</i>.
  476. # If the url is unreadable, nothing is returned.
  477. # To speed up performance, can send an <i>http_token</i> as the
  478. # optional second arg if you know this url is readable.
  479. #
  480. Import private read_url { url {token {}} } {
  481. $self instvar use_http_cache_ cache_ last_modified_
  482. if { $token == {} } {
  483. # rather than checking if { [$self file readable $url] != 1 }
  484. # just copying that code here to speed up performance
  485. if $use_http_cache_ {
  486. if { ![info exists cache_] } {
  487. set cache_ [new HTTPCache]
  488. }
  489. if [info exists last_modified_($url)] {
  490. set buffer [$cache_ get $url 
  491. $last_modified_($url)]
  492. } else {
  493. set buffer [$cache_ get $url]
  494. }
  495. if { $buffer=="" } { unset buffer }
  496. }
  497. if { ![info exists buffer] } {
  498. set token [Http geturl $url]
  499. if { [lindex [set code [::http::code $token]] 1] 
  500. != 200 } {
  501. error "couldn't read "$url": no such file 
  502. or directory (HTTP code $code)"
  503. }
  504. set buffer [::http::data $token]
  505. # destroy the http token
  506. unset $token
  507. # add this to the cache
  508. if $use_http_cache_ {
  509. if { ![info exists cache_] } {
  510. set cache_ [new HTTPCache]
  511. }
  512. $cache_ put $url $buffer
  513. }
  514. }
  515. } else {
  516. set buffer [::http::data $token]
  517. }
  518. return $buffer
  519. }
  520. #
  521. # Eliminates the '..' and '.' in a pathname and returns the absolute
  522. # path of an equivalent filename after following all symbolic links.
  523. # This is useful if the filename was created using ufile join, which
  524. # doesn't evaluate the '..' and '.'. This can also be a useful pre-processing
  525. # of a filename before comparing for equality.
  526. # Note: this procedure is only valid for names of local files that are
  527. # in executable directories.
  528. # The filename is returned if the directory is invalid or not executable.
  529. #
  530. Import private condense_to_absolute_filename { name } {
  531. # XXX: for now, just return $name
  532. return $name
  533. set before_cd [pwd]
  534. # follow symlinks 
  535. while { ![catch "file readlink $filename"] } {
  536. set filename [file readlink $filename]
  537. }
  538. set dirname [$self file dirname $filename]
  539. # XXX: souldn't this also be "$self file tail"?
  540. set tailname [file tail $filename]
  541. set condensed_name $filename
  542. if { ![catch "cd $dirname"] } {
  543. set condensed_name [ufile join [pwd] $tailname]
  544. }
  545. cd $before_cd
  546. return $condensed_name
  547. }
  548. Import private read_file_into_list { filename } {
  549. if [$self is_http_url $filename] {
  550. set buffer [$self read_url $filename]
  551. set lines [split $buffer "n"]
  552. } else {
  553. set f [open $filename "r"]
  554. set lines {}
  555. while 1 {
  556. set line [gets $f]
  557. if [eof $f] {
  558. close $f
  559. break
  560. }
  561. lappend lines "$line"
  562. }
  563. }
  564. return $lines
  565. }
  566. Import private file_readable { args } {
  567. if { [llength $args] == 0 } {
  568. error "wrong # args: should be "$self file
  569. readable name ?arg ...?""
  570. }
  571. set name [lindex $args 0]
  572. if [$self is_http_url $name] {
  573. $self instvar use_http_cache_ cache_ last_modified_
  574. if $use_http_cache_ {
  575. if { ![info exists cache_] } {
  576. set cache_ [new HTTPCache]
  577. }
  578. if [info exists last_modified_($name)] {
  579. set buffer [$cache_ get $name 
  580. $last_modified_($name)]
  581. } else {
  582. set buffer [$cache_ get $name]
  583. }
  584. if { $buffer!={} } {
  585. # XXX: I am creating a dummy http token
  586. # here
  587. $self instvar buf_cnt_
  588. if ![info exists buf_cnt_] {
  589. set buf_cnt_ 0
  590. }
  591. set token ::http::readable_$buf_cnt_
  592. upvar #0 $token state
  593. set state(body) $buffer
  594. incr buf_cnt_
  595. return [list 1 $token]
  596. }
  597. }
  598. if [catch {set token [Http geturl $name]} m] {
  599. return [list 0 "error executing "::http::geturl
  600. $name": $m"]
  601. } else {
  602. set code [::http::code $token]
  603. if {[lindex $code 1] != 200} {
  604. return [list 0 $code]
  605. } else {
  606. if $use_http_cache_ {
  607. if { ![info exists cache_] } {
  608. set cache_ [new HTTPCache]
  609. }
  610. $cache_ put $name [::http::data $token]
  611. }
  612. return [list 1 $token]
  613. }
  614. }
  615. } else {
  616. eval file readable $args
  617. }
  618. #
  619. # the file instproc is basically an enhanced Tcl file proc.
  620. # The diff is that "Import::file readable" & "Import::file dirname" &
  621. # "Import::file join" procedures can handle URLs.
  622. # The only difference from the file proc in behavior is that...
  623. # for URLs, "file readable" returns a list:
  624. #  if readable,   list = { 1 <http-token> }
  625. #  if unreadable, list = { 0 <http-code or other error msg> }
  626. #
  627. # Note that "Import::file join" continues to have the feature that an
  628. # absolute pathname overrides any previous components.
  629. #
  630. Import public file { option args } {
  631. if { $option == "readable" } {
  632. eval [list $self] file_readable $args
  633. } elseif { $option == "dirname" } {
  634. if { [llength $args] == 0 } {
  635. error "wrong # args: should be "$self file
  636. dirname name ?arg ...?""
  637. } else {
  638. set name [lindex $args 0]
  639. if [$self is_http_url $name] {
  640. set url $name
  641. regexp {([^:]+)://([^:/]+)(:([0-9]+))?(/.*)} 
  642. $name url protocol server x 
  643. port trailingpath
  644. if {[string length $trailingpath] == 0} {
  645. set trailingpath /
  646. }
  647. set trailingpath [file dirname "$trailingpath"]
  648. return "$protocol://$server$x$trailingpath"
  649. } else {
  650. eval {file $option} $args
  651. }
  652. }
  653. } elseif { $option == "join" } {
  654. if { [llength $args] == 0 } {
  655. error "wrong # args: should be "$self file
  656. join name ?arg ...?""
  657. } else {
  658. set base_url "[string trimright [lindex $args 0] /]/"
  659. set file_name [lindex $args 1]
  660. if [$self is_http_url $file_name] {
  661. return $file_name
  662. }
  663. if { [$self is_http_url $base_url] && 
  664. [llength $args] ==2 } {
  665. # parse URL into components
  666. regexp {([^:]+)://([^:/]+)(:([0-9]+))?(/.*)} 
  667. $base_url url protocol server 
  668. x port trailingpath
  669. # get rid of initial ./ in file name
  670. regsub -all {^./} $file_name {} file_name
  671. # change any /./ to / in file name
  672. regsub -all {/./} $file_name {/} file_name
  673. # get rid of initial ../ in file name
  674. set counter 0
  675. while [regsub {^../} $file_name {} 
  676. file_name] {
  677. incr counter
  678. }
  679. # for each inital ../ removed, traverse
  680. # up directory tree one level
  681. while { $counter > 0 } {
  682. set trailingpath [$self 
  683. format_as_dir_string 
  684. [$self file dirname 
  685. $trailingpath]]
  686. incr counter -1
  687. }
  688. set trailingpath "[$self format_as_dir_string 
  689. $trailingpath]$file_name"
  690. return "$protocol://$server$x$trailingpath"
  691. } else {
  692. eval {file $option} $args
  693. }
  694. }
  695. } else {
  696. eval {file $option} $args
  697. }
  698. }
  699. #
  700. # Add on a trailing / if not already there.
  701. #
  702. Import private format_as_dir_string { dir_string } {
  703. return "[string trimright [$self file join $dir_string .] .]"
  704. }
  705. #
  706. # Augment existing "source" procedure to get http files if <i>filename</i> starts with http.
  707. #
  708. rename source source.orig
  709. proc source {fileName} {
  710. Import instvar instance_
  711. if ![info exists instance_] {
  712. set instance_ [new Import]
  713. }
  714. if [$instance_ is_http_url $fileName] {
  715. set buffer [$instance_ read_url $fileName]
  716. uplevel eval $buffer
  717. } else {
  718. uplevel source.orig [list $fileName]
  719. }
  720. }
  721. #
  722. # When the Mash interpreter encounters "import <object(s)>", it will
  723. # source the code for the supplied object(s).
  724. #
  725. # If import is explicitly called while we are configured to autoimport_,
  726. # sorry, but we're gonna ignore that and wait for the class to be used before
  727. # we import it.  On the other hand, if we are not configured to autoimport_,
  728. # sure we'll import it for you right now.
  729. #
  730. proc import args {
  731. if { ![catch "Import set autoimport_"] && ![Import set autoimport_] } {
  732. if [catch "really_import $args" error_msg] {
  733. error $error_msg
  734. }
  735. }
  736. }
  737. #
  738. # The first time this import procedure is called, an Import object is created
  739. # and the importTables are read to create a mapping from objects to the
  740. # files in which their class defn and method defns can be found.
  741. #
  742. proc import args {
  743. Import instvar instance_
  744. if ![info exists instance_] {
  745. set instance_ [new Import]
  746. }
  747. if [catch "eval $instance_ import $args" errormsg] {
  748. error $errormsg
  749. }
  750. }
  751. #
  752. # As long as the import procedure has not yet been invoked, the user is
  753. # free to override mappings that may be read from importTables.
  754. #
  755. proc override_importTable_mapping { object file_list } {
  756. Import instvar instance_
  757. if ![info exists instance_] {
  758. set instance_ [new Import]
  759. }
  760. $instance_ override_importTable_mapping $object $file_list
  761. }
  762. proc import_use_http_cache { {yes 1} } {
  763. Import instvar instance_
  764. if ![info exists instance_] {
  765. set instance_ [new Import]
  766. }
  767. $instance_ set use_http_cache_ 1
  768. }