1 # Loads some Tcl-compatible features.
2 # I/O commands, parray, open |..., errorInfo, ::env
3 # try, throw, file copy, file delete -force
5 # (c) 2008 Steve Bennett <steveb@workware.net.au>
8 # Set up the ::env array
11 # Provide Tcl-compatible I/O commands
12 if {[info commands stdout
] ne
""} {
13 # Tcl-compatible I/O commands
14 foreach p
{gets flush close eof seek tell} {
15 proc $p {chan args
} {p
} {
16 tailcall
$chan $p {*}$args
21 # puts is complicated by -nonewline
23 proc puts {{-nonewline {}} {chan stdout
} msg
} {
24 if {${-nonewline} ni
{-nonewline {}}} {
25 tailcall
${-nonewline} puts $msg
27 tailcall
$chan puts {*}${-nonewline} $msg
30 # read is complicated by -nonewline
32 # read chan ?maxchars?
33 # read -nonewline chan
34 proc read {{-nonewline {}} chan
} {
35 if {${-nonewline} ni
{-nonewline {}}} {
36 tailcall
${-nonewline} read {*}${chan
}
38 tailcall
$chan read {*}${-nonewline}
41 proc fconfigure {f args
} {
51 # Just ignore -translation
54 return -code error "fconfigure: unknown option $n"
61 # fileevent isn't needed in Jim, but provide it for compatibility
62 proc fileevent {args
} {
66 # Second, optional argument is a glob pattern
67 # Third, optional argument is a "putter" function
68 proc parray {arrayname
{pattern
*} {puts puts}} {
72 foreach name
[array names a
$pattern]] {
73 if {[string length
$name] > $max} {
74 set max
[string length
$name]
77 incr max
[string length
$arrayname]
79 foreach name
[lsort [array names a
$pattern]] {
80 $puts [format "%-${max}s = %s" $arrayname\($name\) $a($name)]
84 # Implements 'file copy' - single file mode only
85 proc {file copy
} {{force
{}} source target
} {
87 if {$force ni
{{} -force}} {
88 error "bad option \"$force\": should be -force"
91 set in
[open $source rb
]
93 if {[file exists
$target]} {
95 error "error copying \"$source\" to \"$target\": file already exists"
97 # If source and target are the same, nothing to do
98 if {$source eq
$target} {
101 # Hard linked, or case-insensitive filesystem
102 # Note: mingw returns ino=0 for every file :-(
105 if {$ss(dev
) == $ts(dev
) && $ss(ino
) == $ts(ino
) && $ss(ino
)} {
109 set out
[open $target wb
]
112 } on
error {msg opts
} {
120 # 'open "|..." ?mode?" will invoke this wrapper around exec/pipe
121 # Note that we return a lambda that also provides the 'pid' command
122 proc popen
{cmd
{mode r
}} {
125 if {[string match
"w*" $mode]} {
127 set pids
[exec {*}$cmd]
132 set pids
[exec {*}$cmd]
136 lambda
{cmd args
} {f pids
} {
140 if {$cmd eq
"getfd"} {
143 if {$cmd eq
"close"} {
145 # And wait for the child processes to complete
148 lassign
[wait
$p] status
- rc
149 if {$status eq
"CHILDSTATUS"} {
153 set msg
"child process exited abnormally"
155 set msg
"child killed: received signal"
157 set retopts
[list -code error -errorcode [list $status $p $rc] $msg]
161 tailcall
$f $cmd {*}$args
163 } on
error {error opts
} {
170 # A wrapper around 'pid' that can return the pids for 'popen'
171 local
proc pid {{channelId
{}}} {
172 if {$channelId eq
""} {
175 if {[catch {$channelId tell}]} {
176 return -code error "can not find channel named \"$channelId\""
178 if {[catch {$channelId pid} pids
]} {
184 # try/on/finally conceptually similar to Tcl 8.6
186 # Usage: try ?catchopts? script ?onclause ...? ?finallyclause?
189 # catchopts is: options for catch such as -nobreak, -signal
190 # onclause is: on codes {?resultvar? ?optsvar?} script
191 # codes is: a list of return codes (ok, error, etc. or integers), or * for any
192 # finallyclause is: finally script
195 while {[string match
-* [lindex $args 0]]} {
196 set args
[lassign
$args opt
]
200 lappend catchopts
$opt
202 if {[llength $args] == 0} {
203 return -code error {wrong
# args: should be "try ?options? script ?argument ...?"}
205 set args
[lassign
$args script
]
206 set code
[catch -eval {*}$catchopts {uplevel 1 $script} msg opts
]
210 foreach {on codes vars script
} $args {
213 if {!$handled && ($codes eq
"*" ||
[info returncode
$code] in
$codes)} {
214 lassign
$vars msgvar optsvar
219 if {$optsvar ne
""} {
223 # Override any body result
224 set code
[catch {uplevel 1 $script} msg opts
]
229 set finalcode
[catch {uplevel 1 $codes} finalmsg finalopts
]
231 # Override any body or handler result
239 return -code error "try: expected 'on' or 'finally', got '$on'"
250 # Generates an exception with the given code (ok, error, etc. or an integer)
251 # and the given message
252 proc throw
{code
{msg
""}} {
253 return -code $code $msg
256 # Helper for "file delete -force"
257 proc {file delete force
} {path
} {
258 foreach e
[readdir
$path] {
259 file delete
-force $path/$e