** Official Documentation ** http://www.purl.org/tcl/home/man/tcl8.5/TclCmd/source.htm ** Synopsis ** source - Evaluate a file or resource as a Tcl script : '''source''' ?'''-encoding''' ''encodingName''? ''fileName'' : '''source -rsrc''' ''resourceName ?fileName?'' : '''source -rsrcid''' ''resourceId ?fileName?'' ** Description ** This command takes the contents of the specified file or resource and passes it to the Tcl interpreter as a text script. The return value from '''source''' is the return value of the last command executed in the script. If an error occurs in evaluating the contents of the script then the '''source''' command will return that error. If a '''[return]''' command is invoked from within the script then the remainder of the file will be skipped and the '''source''' command will return normally with the result from the '''[return]''' command. The end-of-file character for files is '\32' (^Z) for all platforms. The source command will read files up to this character. This restriction does not exist for the '''[read]''' or '''[gets]''' commands, allowing for files containing code and data segments (scripted documents). If you require a "^Z" in code for string comparison, you can use "\032" or "\u001a", which will be safely substituted by the Tcl interpreter into "^Z". The '''-rsrc''' and '''-rsrcid''' forms of this command are only available on Macintosh computers. These versions of the command allow you to source a script from a '''TEXT''' resource. You may specify what '''TEXT''' resource to source by either name or id. By default Tcl searches all open resource files, which include the current application and any loaded C extensions. Alternatively, you may specify the ''fileName'' where the '''TEXT''' resource can be found. The file is normally read using the [encoding system%|%system encoding], but this can be overridden with the '''-encoding''' option. ** [source] vs [eval] ** From [Changes in Tcl/Tk 8.4%|%Tcl version 8.4%|%] [source] treats ^Z (ASCII 26) as end-of-file, so scripts containing ^Z could behave differently. ** escaping ^Z in a sourced script ** see [\u001a is an end-of-file character in scripts] ** Alternatives ** [http://www.dedasys.com/freesoftware/%|%sourcecache%|%], by [David Welton], acts as source does, but with caching. This is useful, as he notes, "for applications such as web servers running dynamic pages ..." *** Ensure that a file is only sourced once *** [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::sourceOnce ====== [elfring] ''26 Aug 2003'' The discussion about the function "sourceOnce" is a continuation of the feature request "Include source files only once" [http://sourceforge.net/tracker/?func=detail&aid=793779&group_id=10894&atid=360894]. I do not see that the variable "sources" is documented on the manual page [tclvars] [http://tcl.tk/man/tcl8.5/TclCmd/tclvars.htm] 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. *** making use of environment PATH *** [RS]: '''/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" } } ;# RS ====== *** Evaluate channel contents *** ====== proc sourceOpen {channel} { while 1 { append line [gets $channel line] if { [info complete $line ] } { eval $line } if [eof $channel] break } } ====== *** Evalate channel contents in chunks *** [hans] 2008-02-25 [source] reads the whole file into memory first before eval'ing. 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 } ====== Perhaps someone will come along and help out with error handling and plug logic holes here... ** Determining whether the current script was sourced ** [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 One example of this, a library of procs that together compare two files. If called directly: tclsh numcomp.tcl a.inp b.inp the 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]" } ====== ** [return] in a sourced file ** [Arjen Markus] 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. ** Examples ** *** loading data *** You can also source (almost) pure data files like this: ====== array set data [source datafile] ====== where datafile contains: ======none return { 1 {some data} 2 {other data} . ... foo {note that the element name does not have to be numeric} } ====== results in: ======none % parray data data(.) = ... data(1) = some data data(2) = other data data(foo) = note that the element name does not have to be numeric ====== *** Multiple files *** Just [glob] for the files you want, and [source] them all. ====== foreach x [glob -dir $myDirectory *.tcl] { source $x } ====== *** Exclude myself when sourcing everything in my directory *** If you want to have it load all scripts in its own directory, instead of that "source $x" do an "if $x not itself.." <> 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 $data ====== ---- 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. ---- [CMcC] 12Apr2006 Following 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** * [gets] * [read] * [open] * [encoding] * [source with args] * [stepsource.tcl] * [run] pre-processing for source * [Overloaded source for C-style comments] ---- !!!!!! [Tcl syntax help] - [Arts and Crafts of Tcl-Tk Programming] - %| [Category Command] | [Category File] |% !!!!!!