PageRenderTime 43ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/usb-modeswitch-1.2.3/jim/tclcompat.tcl

#
TCL | 279 lines | 208 code | 25 blank | 46 comment | 35 complexity | 28399ab8ead7b817d7900baeaacb35aa MD5 | raw file
Possible License(s): GPL-2.0, AGPL-3.0
  1. # (c) 2008 Steve Bennett <steveb@workware.net.au>
  2. #
  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
  6. # Set up the ::env array
  7. set env [env]
  8. if {[info commands stdout] ne ""} {
  9. # Tcl-compatible I/O commands
  10. foreach p {gets flush close eof seek tell} {
  11. proc $p {chan args} {p} {
  12. tailcall $chan $p {*}$args
  13. }
  14. }
  15. unset p
  16. # puts is complicated by -nonewline
  17. #
  18. proc puts {{-nonewline {}} {chan stdout} msg} {
  19. if {${-nonewline} ni {-nonewline {}}} {
  20. tailcall ${-nonewline} puts $msg
  21. }
  22. tailcall $chan puts {*}${-nonewline} $msg
  23. }
  24. # read is complicated by -nonewline
  25. #
  26. # read chan ?maxchars?
  27. # read -nonewline chan
  28. proc read {{-nonewline {}} chan} {
  29. if {${-nonewline} ni {-nonewline {}}} {
  30. tailcall ${-nonewline} read {*}${chan}
  31. }
  32. tailcall $chan read {*}${-nonewline}
  33. }
  34. proc fconfigure {f args} {
  35. foreach {n v} $args {
  36. switch -glob -- $n {
  37. -bl* {
  38. $f ndelay $v
  39. }
  40. -bu* {
  41. $f buffering $v
  42. }
  43. -tr* {
  44. # Just ignore -translation
  45. }
  46. default {
  47. return -code error "fconfigure: unknown option $n"
  48. }
  49. }
  50. }
  51. }
  52. }
  53. # case var ?in? pattern action ?pattern action ...?
  54. proc case {var args} {
  55. # Skip dummy parameter
  56. if {[lindex $args 0] eq "in"} {
  57. set args [lrange $args 1 end]
  58. }
  59. # Check for single arg form
  60. if {[llength $args] == 1} {
  61. set args [lindex $args 0]
  62. }
  63. # Check for odd number of args
  64. if {[llength $args] % 2 != 0} {
  65. return -code error "extra case pattern with no body"
  66. }
  67. # Internal function to match a value agains a list of patterns
  68. local proc case.checker {value pattern} {
  69. string match $pattern $value
  70. }
  71. foreach {value action} $args {
  72. if {$value eq "default"} {
  73. set do_action $action
  74. continue
  75. } elseif {[lsearch -bool -command case.checker $value $var]} {
  76. set do_action $action
  77. break
  78. }
  79. }
  80. if {[info exists do_action]} {
  81. set rc [catch [list uplevel 1 $do_action] result opts]
  82. if {$rc} {
  83. incr opts(-level)
  84. }
  85. return {*}$opts $result
  86. }
  87. }
  88. # fileevent isn't needed in Jim, but provide it for compatibility
  89. proc fileevent {args} {
  90. tailcall {*}$args
  91. }
  92. # Second, option argument is a glob pattern
  93. # Third, optional argument is a "putter" function
  94. #
  95. proc parray {arrayname {pattern *} {puts puts}} {
  96. upvar $arrayname a
  97. set max 0
  98. foreach name [array names a $pattern]] {
  99. if {[string length $name] > $max} {
  100. set max [string length $name]
  101. }
  102. }
  103. incr max [string length $arrayname]
  104. incr max 2
  105. foreach name [lsort [array names a $pattern]] {
  106. $puts [format "%-${max}s = %s" $arrayname\($name\) $a($name)]
  107. }
  108. }
  109. # Implements 'file copy' - single file mode only
  110. proc {file copy} {{force {}} source target} {
  111. try {
  112. if {$force ni {{} -force}} {
  113. error "bad option \"$force\": should be -force"
  114. }
  115. set in [open $source]
  116. if {$force eq "" && [file exists $target]} {
  117. $in close
  118. error "error copying \"$source\" to \"$target\": file already exists"
  119. }
  120. set out [open $target w]
  121. $in copyto $out
  122. $out close
  123. } on error {msg opts} {
  124. incr opts(-level)
  125. return {*}$opts $msg
  126. } finally {
  127. catch {$in close}
  128. }
  129. }
  130. # 'open "|..." ?mode?" will invoke this wrapper around exec/pipe
  131. # Note that we return a lambda which also provides the 'pid' command
  132. proc popen {cmd {mode r}} {
  133. lassign [socket pipe] r w
  134. try {
  135. if {[string match "w*" $mode]} {
  136. lappend cmd <@$r &
  137. set pids [exec {*}$cmd]
  138. $r close
  139. set f $w
  140. } else {
  141. lappend cmd >@$w &
  142. set pids [exec {*}$cmd]
  143. $w close
  144. set f $r
  145. }
  146. lambda {cmd args} {f pids} {
  147. if {$cmd eq "pid"} {
  148. return $pids
  149. }
  150. if {$cmd eq "close"} {
  151. $f close
  152. # And wait for the child processes to complete
  153. foreach p $pids { os.wait $p }
  154. return
  155. }
  156. tailcall $f $cmd {*}$args
  157. }
  158. } on error {error opts} {
  159. $r close
  160. $w close
  161. error $error
  162. }
  163. }
  164. # A wrapper around 'pid' which can return the pids for 'popen'
  165. local proc pid {{chan {}}} {
  166. if {$chan eq ""} {
  167. tailcall upcall pid
  168. }
  169. if {[catch {$chan tell}]} {
  170. return -code error "can not find channel named \"$chan\""
  171. }
  172. if {[catch {$chan pid} pids]} {
  173. return ""
  174. }
  175. return $pids
  176. }
  177. # try/on/finally conceptually similar to Tcl 8.6
  178. #
  179. # Usage: try ?catchopts? script ?onclause ...? ?finallyclause?
  180. #
  181. # Where:
  182. # onclause is: on codes {?resultvar? ?optsvar?} script
  183. #
  184. # codes is: a list of return codes (ok, error, etc. or integers), or * for any
  185. #
  186. # finallyclause is: finally script
  187. #
  188. #
  189. # Where onclause is: on codes {?resultvar? ?optsvar?}
  190. proc try {args} {
  191. set catchopts {}
  192. while {[string match -* [lindex $args 0]]} {
  193. set args [lassign $args opt]
  194. if {$opt eq "--"} {
  195. break
  196. }
  197. lappend catchopts $opt
  198. }
  199. if {[llength $args] == 0} {
  200. return -code error {wrong # args: should be "try ?options? script ?argument ...?"}
  201. }
  202. set args [lassign $args script]
  203. set code [catch -eval {*}$catchopts [list uplevel 1 $script] msg opts]
  204. set handled 0
  205. foreach {on codes vars script} $args {
  206. switch -- $on \
  207. on {
  208. if {!$handled && ($codes eq "*" || [info returncode $code] in $codes)} {
  209. lassign $vars msgvar optsvar
  210. if {$msgvar ne ""} {
  211. upvar $msgvar hmsg
  212. set hmsg $msg
  213. }
  214. if {$optsvar ne ""} {
  215. upvar $optsvar hopts
  216. set hopts $opts
  217. }
  218. # Override any body result
  219. set code [catch [list uplevel 1 $script] msg opts]
  220. incr handled
  221. }
  222. } \
  223. finally {
  224. set finalcode [catch [list uplevel 1 $codes] finalmsg finalopts]
  225. if {$finalcode} {
  226. # Override any body or handler result
  227. set code $finalcode
  228. set msg $finalmsg
  229. set opts $finalopts
  230. }
  231. break
  232. } \
  233. default {
  234. return -code error "try: expected 'on' or 'finally', got '$on'"
  235. }
  236. }
  237. if {$code} {
  238. incr opts(-level)
  239. return {*}$opts $msg
  240. }
  241. return $msg
  242. }
  243. # Generates an exception with the given code (ok, error, etc. or an integer)
  244. # and the given message
  245. proc throw {code {msg ""}} {
  246. return -code $code $msg
  247. }
  248. # Helper for "file delete -force"
  249. proc {file delete force} {path} {
  250. foreach e [readdir $path] {
  251. file delete -force $path/$e
  252. }
  253. file delete $path
  254. }