commit a copy of snit
[scpubgit/TenDotTcl.git] / snit / snit_tcl83_utils.tcl
1 #--------------------------------------------------------------------------
2 # TITLE:
3 #       snit_tcl83_utils.tcl
4 #
5 # AUTHOR:
6 #       Kenneth Green, 28 Aug 2004
7 #
8 # DESCRIPTION:
9 #       Utilities to support the back-port of snit from Tcl 8.4 to 8.3
10 #
11 #--------------------------------------------------------------------------
12 # Copyright
13 #
14 # Copyright (c) 2005 Kenneth Green
15 # Modified by Andreas Kupries.
16 # All rights reserved. This code is licensed as described in license.txt.
17 #--------------------------------------------------------------------------
18 # This code is freely distributable, but is provided as-is with
19 # no warranty expressed or implied.
20 #--------------------------------------------------------------------------
21 # Acknowledgements
22 #   The changes described in this file are made to the awesome 'snit'
23 #   library as provided by William H. Duquette under the terms
24 #   defined in the associated 'license.txt'.
25 #-----------------------------------------------------------------------
26
27 #-----------------------------------------------------------------------
28 # Namespace
29
30 namespace eval ::snit83 {}
31
32 #-----------------------------------------------------------------------
33 # Some Snit83 variables
34
35 namespace eval ::snit83 {
36     variable  cmdTraceTable
37     array set cmdTraceTable {}
38
39     namespace eval private {}
40 }
41
42
43 #-----------------------------------------------------------------------
44 # Initialisation
45
46 #
47 # Override Tcl functions so we can mimic some behaviours. This is
48 # conditional on not having been done already. Otherwise loading snit
49 # twice will fail the second time.
50 #
51
52 if [info exists tk_version] {
53     if {
54         ![llength [info procs destroy]] ||
55         ![regexp snit83 [info body destroy]]
56     } {
57         rename destroy __destroy__
58     }
59 }
60 if {
61     ![llength [info procs namespace]] ||
62     ![regexp snit83 [info body namespace]]
63 } {
64     rename namespace __namespace__
65     rename rename    __rename__ ;# must be last one renamed!
66 }
67
68 #-----------------------------------------------------------------------
69 # Global namespace functions
70
71
72 # destroy -
73 #
74 # Perform delete tracing and then invoke the actual Tk destroy command
75
76 if [info exists tk_version] {
77     proc destroy { w } {
78         variable ::snit83::cmdTraceTable
79
80         set index "delete,$w"
81         if [info exists cmdTraceTable($index)] {
82             set cmd $cmdTraceTable($index)
83             ::unset cmdTraceTable($index) ;# prevent recursive tracing
84             if [catch {eval $cmd $oldName \"$newName\" delete} err] { ; # "
85                 error $err
86             }
87         }
88
89         return [__destroy__ $w]
90     }
91 }
92
93 # namespace -
94 #
95 # Add limited support for 'namespace exists'. Must be a fully
96 # qualified namespace name (pattern match support not provided).
97
98 proc namespace { cmd args } {
99     if {[string equal $cmd "exists"]} {
100         set ptn [lindex $args 0]
101         return [::snit83::private::NamespaceIsDescendantOf :: $ptn]
102     } elseif {[string equal $cmd "delete"]} {
103         if [namespace exists [lindex $args 0]] {
104             return [uplevel 1 [subst {__namespace__ $cmd $args}]]
105         }
106     } else {
107         return [uplevel 1 [subst {__namespace__ $cmd $args}]]
108     }
109 }
110
111 # rename -
112 #
113 # Perform rename tracing and then invoke the actual Tcl rename command
114
115 proc rename { oldName newName } {
116     variable ::snit83::cmdTraceTable
117
118     # Get caller's namespace since rename must be performed
119     # in the context of the caller's namespace
120     set callerNs "::"
121     set callerLevel [expr {[info level] - 1}]
122     if { $callerLevel > 0 } {
123         set callerInfo [info level $callerLevel]
124         set procName   [lindex $callerInfo 0]
125         set callerNs   [namespace qualifiers $procName]
126     }
127
128     #puts "rename: callerNs: $callerNs"
129     #puts "rename: '$oldName' -> '$newName'"
130     #puts "rename: rcds - [join [array names cmdTraceTable] "\nrename: rcds - "]"
131
132     set result [namespace eval $callerNs [concat __rename__ [list $oldName $newName]]]
133
134     set index1 "rename,$oldName"
135     set index2 "rename,::$oldName"
136
137     foreach index [list $index1 $index2] {
138         if [info exists cmdTraceTable($index)] {
139             set cmd $cmdTraceTable($index)
140
141             #puts "rename: '$cmd' { $oldName -> $newName }"
142
143             ::unset cmdTraceTable($index) ;# prevent recursive tracing
144             if {![string equal $newName ""]} {
145                 # Create a new trace record under the new name
146                 set cmdTraceTable(rename,$newName) $cmd
147             }
148             if [catch {eval $cmd $oldName \"$newName\" rename} err] {
149                 error $err
150             }
151             break
152         }
153     }
154
155     return $result
156 }
157
158
159 #-----------------------------------------------------------------------
160 # Private functions
161
162 proc ::snit83::private::NamespaceIsDescendantOf { parent child } {
163     set result 0
164
165     foreach ns [__namespace__ children $parent] {
166         if [string match $ns $child] {
167             set result 1
168             break;
169         } else {
170             if [set result [NamespaceIsDescendantOf $ns $child]] {
171                 break
172             }
173         }
174     }
175     return $result
176 }
177
178
179 #-----------------------------------------------------------------------
180 # Utility functions
181
182 proc ::snit83::traceAddCommand {name ops command} {
183     variable cmdTraceTable
184
185     #puts "::snit83::traceAddCommand n/$name/ o/$ops/ c/$command/"
186     #puts "XX [join [array names cmdTraceTable] "\nXX "]"
187
188     foreach op $ops {
189         set index "$op,$name"
190         #puts "::snit83::traceAddCommand: index = $index cmd = $command"
191
192         set cmdTraceTable($index) $command
193     }
194 }
195
196 proc ::snit83::traceRemoveCommand {name ops command} {
197     variable cmdTraceTable
198
199     #puts "::snit83::traceRemoveCommand n/$name/ o/$ops/ c/$command/"
200     #puts "YY [join [array names cmdTraceTable] "\nYY "]"
201
202     foreach op $ops {
203         set index "$op,$name"
204         #puts "::snit83::traceRemoveCommand: index = $index cmd = $command"
205
206         catch { ::unset cmdTraceTable($index) }
207     }
208 }
209
210 # Add support for 'unset -nocomplain'
211 proc ::snit83::unset { args } {
212
213     #puts "::snit83::unset - args: '$args'"
214
215     set noComplain 0
216     if {[string equal [lindex $args 0] "-nocomplain"]} {
217         set noComplain 1
218         set args [lrange $args 1 end]
219     }
220     if {[string equal [lindex $args 0] "--"]} {
221         set args [lrange $args 1 end]
222     }
223
224     if [catch {
225         uplevel 1 [linsert $args 0 ::unset]
226     } err] {
227         if { !$noComplain } {
228             error $err
229         }
230     }
231 }