- source ?-encoding encodingName? fileName
- source -rsrc resourceName ?fileName?
- source -rsrcid resourceId ?fileName?
Note that, with Tcl 8.4, the source command is documented as treating a ^Z (the literal character, not the 2 ascii representation I just used) as an indicator of end of file across all platforms. There is verbage there that reflects what to do if you need to use that character in a data string. \u001a is an end-of-file character in scripts.
Notice that sourcing is always done in the system encoding. For sources in Unicode or other goodies you'd have to open a file, fconfigure -encoding, read, eval, close - see Unicode file reader or source with encoding. source $file does basically:
In a general sense, this command feeds commands to Tcl similarly to if you were typing them at the Tcl interpreter prompt.Can anyone detail some of the Tcl behavioral differences between a user typing in the commands and invoking source?
David Welton's sourcecache [1] acts as source does, but with caching. This is useful, as he notes, "for applications such as web servers running dynamic pages ..."
Arjen Markus Sometimes it is useful to distinguish between the Tcl/Tk shell sourcing the file (as in tclsh aa.tcl) and the script being evaluated to do this (via the command "source aa.tcl"). This can be used to:
/bin/sh-like source: When playing Bourne shell, I found out that their source command "." searches the environment PATH, a feature that has occasionally been missed in Tcl's source. Well, here's an emulation (that caters for Unix/Win differences as well):
escargo 13 Dec 2002 - I think it is interesting how source takes a filename and not a channel. Certainly as a convenience, having the source command open the file, read the file, and close the file factors out common behaviour, but it hides the low-level operation of reading the file and executing the contents. Is there a particular reason this lower abstraction is not available? (Perhaps it's too trivial, but it seems to me that being able to source a channel could be a powerful thing, maybe too powerful. But then, maybe that's why there are safe interpreters.)DGP Not available?! What part of read and eval are you not aware of?escargo 25 Aug 2003 - Sorry; I didn't mean to ignore your question. I am aware of read and eval. However, I was not saying that the equivalent to source for reading a channel was not constructable, only that I would have expected source to be a wrapper (which would be responsible for opening and closing the file) around an existing, defined, lower-level operation on a channel. I suspect that files existed before channels, and that retrofitting a new, lower-level channel-oriented function was not seen as sufficiently worth doing.
So, for a counterpart for source, which reads an open channel rather than takes a file name as an argument, all one needs is something like:
Looking at memory usage shows that source reads the whole file into memory first before eval'ing. Now, I'm saving updates to a large tcl source file whenever any vars change, so the "tape" file can become quite large. Of course the memory is freed after sourcing, but still there is a huge spike.Here's my solution. After every update to my data.tcl file, I'm inserting a line with: "#~". Of course, I have to escape that special char everywhere. Now it's easy to read and eval the huge file in chunks, timing is very close to that of source.
Is there any sort of path used by source when trying to locate fileName? - RS: No, the absolute or relative path must be specified. But see the proc "." above, which is PATH-aware (maybe rename it to something less dangerous for Tk...).
Joe Mistachkin 25/Aug/2003 - The following proc will source a file only once:
source-ing a wildcard list of files:Just glob for the files you want, and source them all.
Notice that sourcing is always done in the system encoding. For sources in Unicode or other goodies you'd have to open a file, fconfigure -encoding, read, eval, close - see Unicode file reader or source with encoding. source $file does basically:
set fp [open $file] set data [read $fp] close $fp eval $dataYou can also source (almost) pure data files like this:
array set data [source datafile]where datafile contains:
return {
1 {some data}
2 {other data}
. ...
foo {note that the element name does not have to be numeric}
}results in:% parray data data(.) = ... data(1) = some data data(2) = other data data(foo) = note that the element name does not have to be numeric
In a general sense, this command feeds commands to Tcl similarly to if you were typing them at the Tcl interpreter prompt.Can anyone detail some of the Tcl behavioral differences between a user typing in the commands and invoking source?
David Welton's sourcecache [1] acts as source does, but with caching. This is useful, as he notes, "for applications such as web servers running dynamic pages ..."
Arjen Markus Sometimes it is useful to distinguish between the Tcl/Tk shell sourcing the file (as in tclsh aa.tcl) and the script being evaluated to do this (via the command "source aa.tcl"). This can be used to:
- Provide a library of useful procedures and
- Provide a self-contained "main" proc in the same file
tclsh numcomp.tcl a.inp b.inpthe main proc gets executed and the comparison is started. If sourced inside another script, however, this is not done and the sourcing script has full control. (In this case: it is a larger script controlling a number of programs whose results must be compared against reference files).The trick is the code below:
if { [info script] == "$::argv0" } {
puts "In main"
} else {
puts "Sourcing [info script]"
}Another trick I recently learned: the return command works in source as in any other script - it allows you to return from the source command, so that the rest of the file is not handled. Quite useful for recovering from errors or making the processing depend on external influences./bin/sh-like source: When playing Bourne shell, I found out that their source command "." searches the environment PATH, a feature that has occasionally been missed in Tcl's source. Well, here's an emulation (that caters for Unix/Win differences as well):
proc path'separator {} {
switch -- $::tcl_platform(platform) {
unix {return ":"}
windows {return ";"}
default {error "unknown platform"}
}
}
if {[llength [info global tk*]]==0} {
# The name . is taboo in a wish, would exit immediately
proc . filename {
foreach path [split $::env(PATH) [path'separator]] {
set try [file join $path $filename]
if [file readable $try] {return [source $try]}
}
error ".: no such file or directory: $filename"
}
} ;# RSescargo 13 Dec 2002 - I think it is interesting how source takes a filename and not a channel. Certainly as a convenience, having the source command open the file, read the file, and close the file factors out common behaviour, but it hides the low-level operation of reading the file and executing the contents. Is there a particular reason this lower abstraction is not available? (Perhaps it's too trivial, but it seems to me that being able to source a channel could be a powerful thing, maybe too powerful. But then, maybe that's why there are safe interpreters.)DGP Not available?! What part of read and eval are you not aware of?escargo 25 Aug 2003 - Sorry; I didn't mean to ignore your question. I am aware of read and eval. However, I was not saying that the equivalent to source for reading a channel was not constructable, only that I would have expected source to be a wrapper (which would be responsible for opening and closing the file) around an existing, defined, lower-level operation on a channel. I suspect that files existed before channels, and that retrofitting a new, lower-level channel-oriented function was not seen as sufficiently worth doing.
So, for a counterpart for source, which reads an open channel rather than takes a file name as an argument, all one needs is something like:
proc sourceOpen {channel} {
while 1 {
append line [gets $channel line]
if { [info complete $line ] } {
eval $line
}
if [eof $channel] break
}
}Perhaps someone will come along and help out with error handling and plug logic holes here...Looking at memory usage shows that source reads the whole file into memory first before eval'ing. Now, I'm saving updates to a large tcl source file whenever any vars change, so the "tape" file can become quite large. Of course the memory is freed after sourcing, but still there is a huge spike.Here's my solution. After every update to my data.tcl file, I'm inserting a line with: "#~". Of course, I have to escape that special char everywhere. Now it's easy to read and eval the huge file in chunks, timing is very close to that of source.
proc load_data {fname} {
set f [open $fname r]
fconfigure $f -translation binary \
-buffersize 1000000 -encoding "iso8859-1"
while {![eof $f]} {
set r [read $f 1000000]
set l [string length $r]
set c [string last "~" $r]
while {$c eq -1} {
if {[eof $f]} { error "Missing ~" }
set r2 [read $f 1000000]
append r $r2
set l [string length $r]
set c [string last "~" $r]
}
set e [string range $r 0 $c]
uplevel #0 eval $e
seek $f [expr $c-$l-1] current
if {$l<1000000} { break }
}
close $f
}-hans 2008-02-25Is there any sort of path used by source when trying to locate fileName? - RS: No, the absolute or relative path must be specified. But see the proc "." above, which is PATH-aware (maybe rename it to something less dangerous for Tk...).
Joe Mistachkin 25/Aug/2003 - The following proc will source a file only once:
proc sourceOnce { file } {
upvar "1" sources sources
if {![info exists sources([file normalize $file])]} then {
# don't catch errors, since that may indicate we failed to load it...?
uplevel "1" [list source $file]
# mark it as loaded since it was source'd with no error...
set sources([file normalize $file]) "1"
}
}DGP I don't think that quite works. Different callers might well refer to different sources arrays. Better to encapsulate all in the same namespace.
namespace eval my {
namespace export sourceOnce
variable sources
array set sources {}
proc sourceOnce { file } {
# Remaining exercise for the next reader. Adapt argument
# processing to support the -rsrc and -encoding options
# that [::source] provides (or will in Tcl 8.5)
variable sources
if {![info exists sources([file normalize $file])]} then {
# don't catch errors, since that may indicate we failed to load it...?
# Extra challenge: Use the techniques outlined in TIP 90
# to catch errors, then re-raise them so the [uplevel] does
# not appear on the stack trace.
# We don't know what command is [source] in the caller's context,
# so fully qualify to get the [::source] we want.
uplevel 1 [list ::source $file]
# mark it as loaded since it was source'd with no error...
set sources([file normalize $file]) 1
}
}
}
namespace import my::sourceOnceelfring 26 Aug 2003 The discussion about the function "sourceOnce" is a continuation of the feature request "Include source files only once" [2]. I do not see that the variable "sources" is documented on the manual page tclvars [3] or anywhere else. Will this happen with Tcl 8.5 or earlier? I get the feeling that the requested addition of a function "include_once" or "source -once" need more safety checks and conditions than I have expected. Who would like to show the standard argument processing for it?DKF (same day) - It's not documented anywhere in the core because it is not in the core. It's defined right here, just a few lines above. :^)RS (same day too) Note that source is the weakest way of how to change a running system. Relying on auto_index or package mechanisms is more robust, and easier once set up - do nothing (maybe extend auto_path), or package require.elfring 27 Aug 2003 Well, thank you for this fact. - The current implementation of the function "source" does not record into a variable which files were read.Will the argument processing be a base function or class in TCL 8.5 so that other implementations can reuse it? I do not want to duplicate the stuff for the option "-rsrc...".LV I don't know of any TIP to refactor argument processing into a TclOO class. In fact, I don't know of any TIP to refactor any Tcl functionality into a TclOO class or base function.source-ing a wildcard list of files:Just glob for the files you want, and source them all.
foreach x [glob -dir $myDirectory *.tcl] {
source $x
}If you want to have it load all scripts in its own directory, instead of that "source $x" do an "if $x not itself.."CMcC 12Apr2006Following discussions on tcler's chat in which dgp suggested (on, I presume, aesthetic grounds) that pkgIndex.tcl ought not to contain more than one package definition:One implementation problem with advice is that a package may contain many source files, but there is no way to discover the directory within which those source files reside.One possible solution would be to have the pkgIndex.tcl file entry for the multi-source package source all of the components of the package.The most elegant way to achieve this goal would be to allow source to take multiple file arguments as follows: source ?-dir directory? fileName ?fileName?, and this is what I propose for general consideration.See also edit
- gets
- read
- open
- encoding
- source with args
- stepsource.tcl
- run pre-processing for source
- Overloaded source for C-style comments
