bench.tcl: fix the pi benchmark
[jimtcl.git] / tclcompat.tcl
blob84d9d25eb81183cc038790af922731fb4c17b9bd
1 # (c) 2008 Steve Bennett <steveb@workware.net.au>
3 # Loads some Tcl-compatible features.
4 # I/O commands, case, lassign, parray, errorInfo, ::tcl_platform, ::env
5 # try, throw, file copy, file delete -force
7 # Set up the ::env array
8 set env [env]
10 if {[info commands stdout] ne ""} {
11 # Tcl-compatible I/O commands
12 foreach p {gets flush close eof seek tell} {
13 proc $p {chan args} {p} {
14 tailcall $chan $p {*}$args
17 unset p
19 # puts is complicated by -nonewline
21 proc puts {{-nonewline {}} {chan stdout} msg} {
22 if {${-nonewline} ni {-nonewline {}}} {
23 tailcall ${-nonewline} puts $msg
25 tailcall $chan puts {*}${-nonewline} $msg
28 # read is complicated by -nonewline
30 # read chan ?maxchars?
31 # read -nonewline chan
32 proc read {{-nonewline {}} chan} {
33 if {${-nonewline} ni {-nonewline {}}} {
34 tailcall ${-nonewline} read {*}${chan}
36 tailcall $chan read {*}${-nonewline}
39 proc fconfigure {f args} {
40 foreach {n v} $args {
41 switch -glob -- $n {
42 -bl* {
43 $f ndelay $(!$v)
45 -bu* {
46 $f buffering $v
48 -tr* {
49 # Just ignore -translation
51 default {
52 return -code error "fconfigure: unknown option $n"
59 # fileevent isn't needed in Jim, but provide it for compatibility
60 proc fileevent {args} {
61 tailcall {*}$args
64 # Second, option argument is a glob pattern
65 # Third, optional argument is a "putter" function
67 proc parray {arrayname {pattern *} {puts puts}} {
68 upvar $arrayname a
70 set max 0
71 foreach name [array names a $pattern]] {
72 if {[string length $name] > $max} {
73 set max [string length $name]
76 incr max [string length $arrayname]
77 incr max 2
78 foreach name [lsort [array names a $pattern]] {
79 $puts [format "%-${max}s = %s" $arrayname\($name\) $a($name)]
83 # Implements 'file copy' - single file mode only
84 proc {file copy} {{force {}} source target} {
85 try {
86 if {$force ni {{} -force}} {
87 error "bad option \"$force\": should be -force"
90 set in [open $source]
92 if {[file exists $target]} {
93 if {$force eq ""} {
94 error "error copying \"$source\" to \"$target\": file already exists"
96 # If source and target are the same, nothing to do
97 if {$source eq $target} {
98 return
100 # Hard linked, or case-insensitive filesystem
101 # Note: mingw returns ino=0 for every file :-(
102 file stat $source ss
103 file stat $target ts
104 if {$ss(dev) == $ts(dev) && $ss(ino) == $ts(ino) && $ss(ino)} {
105 return
108 set out [open $target w]
109 $in copyto $out
110 $out close
111 } on error {msg opts} {
112 incr opts(-level)
113 return {*}$opts $msg
114 } finally {
115 catch {$in close}
119 # 'open "|..." ?mode?" will invoke this wrapper around exec/pipe
120 # Note that we return a lambda which also provides the 'pid' command
121 proc popen {cmd {mode r}} {
122 lassign [socket pipe] r w
123 try {
124 if {[string match "w*" $mode]} {
125 lappend cmd <@$r &
126 set pids [exec {*}$cmd]
127 $r close
128 set f $w
129 } else {
130 lappend cmd >@$w &
131 set pids [exec {*}$cmd]
132 $w close
133 set f $r
135 lambda {cmd args} {f pids} {
136 if {$cmd eq "pid"} {
137 return $pids
139 if {$cmd eq "close"} {
140 $f close
141 # And wait for the child processes to complete
142 foreach p $pids { os.wait $p }
143 return
145 tailcall $f $cmd {*}$args
147 } on error {error opts} {
148 $r close
149 $w close
150 error $error
154 # A wrapper around 'pid' which can return the pids for 'popen'
155 local proc pid {{channelId {}}} {
156 if {$channelId eq ""} {
157 tailcall upcall pid
159 if {[catch {$channelId tell}]} {
160 return -code error "can not find channel named \"$channelId\""
162 if {[catch {$channelId pid} pids]} {
163 return ""
165 return $pids
168 # try/on/finally conceptually similar to Tcl 8.6
170 # Usage: try ?catchopts? script ?onclause ...? ?finallyclause?
172 # Where:
173 # onclause is: on codes {?resultvar? ?optsvar?} script
175 # codes is: a list of return codes (ok, error, etc. or integers), or * for any
177 # finallyclause is: finally script
180 # Where onclause is: on codes {?resultvar? ?optsvar?}
181 proc try {args} {
182 set catchopts {}
183 while {[string match -* [lindex $args 0]]} {
184 set args [lassign $args opt]
185 if {$opt eq "--"} {
186 break
188 lappend catchopts $opt
190 if {[llength $args] == 0} {
191 return -code error {wrong # args: should be "try ?options? script ?argument ...?"}
193 set args [lassign $args script]
194 set code [catch -eval {*}$catchopts [list uplevel 1 $script] msg opts]
196 set handled 0
198 foreach {on codes vars script} $args {
199 switch -- $on \
200 on {
201 if {!$handled && ($codes eq "*" || [info returncode $code] in $codes)} {
202 lassign $vars msgvar optsvar
203 if {$msgvar ne ""} {
204 upvar $msgvar hmsg
205 set hmsg $msg
207 if {$optsvar ne ""} {
208 upvar $optsvar hopts
209 set hopts $opts
211 # Override any body result
212 set code [catch [list uplevel 1 $script] msg opts]
213 incr handled
216 finally {
217 set finalcode [catch [list uplevel 1 $codes] finalmsg finalopts]
218 if {$finalcode} {
219 # Override any body or handler result
220 set code $finalcode
221 set msg $finalmsg
222 set opts $finalopts
224 break
226 default {
227 return -code error "try: expected 'on' or 'finally', got '$on'"
231 if {$code} {
232 incr opts(-level)
233 return {*}$opts $msg
235 return $msg
238 # Generates an exception with the given code (ok, error, etc. or an integer)
239 # and the given message
240 proc throw {code {msg ""}} {
241 return -code $code $msg
244 # Helper for "file delete -force"
245 proc {file delete force} {path} {
246 foreach e [readdir $path] {
247 file delete -force $path/$e
249 file delete $path