1 |
4 |
doru |
#!/bin/sh
|
2 |
|
|
# \
|
3 |
|
|
exec wish "$0" ${1+"$@"}
|
4 |
|
|
|
5 |
|
|
## tkcon.tcl
|
6 |
|
|
## Enhanced Tk Console, part of the VerTcl system
|
7 |
|
|
##
|
8 |
|
|
## Originally based off Brent Welch's Tcl Shell Widget
|
9 |
|
|
## (from "Practical Programming in Tcl and Tk")
|
10 |
|
|
##
|
11 |
|
|
## Thanks to the following (among many) for early bug reports & code ideas:
|
12 |
|
|
## Steven Wahl <steven@indra.com>, Jan Nijtmans <nijtmans@nici.kun.nl>
|
13 |
|
|
## Crimmins <markcrim@umich.edu>, Wart <wart@ugcs.caltech.edu>
|
14 |
|
|
##
|
15 |
|
|
## Copyright 1995-2001 Jeffrey Hobbs
|
16 |
|
|
## Initiated: Thu Aug 17 15:36:47 PDT 1995
|
17 |
|
|
##
|
18 |
|
|
## jeff.hobbs@acm.org, jeff@hobbs.org
|
19 |
|
|
##
|
20 |
|
|
## source standard_disclaimer.tcl
|
21 |
|
|
## source bourbon_ware.tcl
|
22 |
|
|
##
|
23 |
|
|
|
24 |
|
|
# Proxy support for retrieving the current version of Tkcon.
|
25 |
|
|
#
|
26 |
|
|
# Mon Jun 25 12:19:56 2001 - Pat Thoyts <Pat.Thoyts@bigfoot.com>
|
27 |
|
|
#
|
28 |
|
|
# In your tkcon.cfg or .tkconrc file put your proxy details into the
|
29 |
|
|
# `proxy' member of the `PRIV' array. e.g.:
|
30 |
|
|
#
|
31 |
|
|
# set ::tkcon::PRIV(proxy) wwwproxy:8080
|
32 |
|
|
#
|
33 |
|
|
# If you want to be prompted for proxy authentication details (eg for
|
34 |
|
|
# an NT proxy server) make the second element of this variable non-nil - eg:
|
35 |
|
|
#
|
36 |
|
|
# set ::tkcon::PRIV(proxy) {wwwproxy:8080 1}
|
37 |
|
|
#
|
38 |
|
|
# Or you can set the above variable from within tkcon by calling
|
39 |
|
|
#
|
40 |
|
|
# tkcon master set ::tkcon:PRIV(proxy) wwwproxy:8080
|
41 |
|
|
#
|
42 |
|
|
|
43 |
|
|
if {$tcl_version < 8.0} {
|
44 |
|
|
return -code error "tkcon requires at least Tcl/Tk8"
|
45 |
|
|
} else {
|
46 |
|
|
package require -exact Tk $tcl_version
|
47 |
|
|
}
|
48 |
|
|
|
49 |
|
|
catch {package require bogus-package-name}
|
50 |
|
|
foreach pkg [info loaded {}] {
|
51 |
|
|
set file [lindex $pkg 0]
|
52 |
|
|
set name [lindex $pkg 1]
|
53 |
|
|
if {![catch {set version [package require $name]}]} {
|
54 |
|
|
if {[string match {} [package ifneeded $name $version]]} {
|
55 |
|
|
package ifneeded $name $version [list load $file $name]
|
56 |
|
|
}
|
57 |
|
|
}
|
58 |
|
|
}
|
59 |
|
|
catch {unset pkg file name version}
|
60 |
|
|
|
61 |
|
|
# Tk 8.4 makes previously exposed stuff private.
|
62 |
|
|
# FIX: Update tkcon to not rely on the private Tk code.
|
63 |
|
|
#
|
64 |
|
|
if {![llength [info globals tkPriv]]} {
|
65 |
|
|
::tk::unsupported::ExposePrivateVariable tkPriv
|
66 |
|
|
}
|
67 |
|
|
foreach cmd {SetCursor UpDownLine Transpose ScrollPages} {
|
68 |
|
|
if {![llength [info commands tkText$cmd]]} {
|
69 |
|
|
::tk::unsupported::ExposePrivateCommand tkText$cmd
|
70 |
|
|
}
|
71 |
|
|
}
|
72 |
|
|
|
73 |
|
|
# Initialize the ::tkcon namespace
|
74 |
|
|
#
|
75 |
|
|
namespace eval ::tkcon {
|
76 |
|
|
# The OPT variable is an array containing most of the optional
|
77 |
|
|
# info to configure. COLOR has the color data.
|
78 |
|
|
variable OPT
|
79 |
|
|
variable COLOR
|
80 |
|
|
|
81 |
|
|
# PRIV is used for internal data that only tkcon should fiddle with.
|
82 |
|
|
variable PRIV
|
83 |
|
|
set PRIV(WWW) [info exists embed_args]
|
84 |
|
|
}
|
85 |
|
|
|
86 |
|
|
## ::tkcon::Init - inits tkcon
|
87 |
|
|
#
|
88 |
|
|
# Calls: ::tkcon::InitUI
|
89 |
|
|
# Outputs: errors found in tkcon's resource file
|
90 |
|
|
##
|
91 |
|
|
proc ::tkcon::Init {} {
|
92 |
|
|
variable OPT
|
93 |
|
|
variable COLOR
|
94 |
|
|
variable PRIV
|
95 |
|
|
global tcl_platform env argc argv tcl_interactive errorInfo
|
96 |
|
|
|
97 |
|
|
if {![info exists argv]} {
|
98 |
|
|
set argv {}
|
99 |
|
|
set argc 0
|
100 |
|
|
}
|
101 |
|
|
|
102 |
|
|
set tcl_interactive 1
|
103 |
|
|
|
104 |
|
|
if {[info exists PRIV(name)]} {
|
105 |
|
|
set title $PRIV(name)
|
106 |
|
|
} else {
|
107 |
|
|
MainInit
|
108 |
|
|
# some main initialization occurs later in this proc,
|
109 |
|
|
# to go after the UI init
|
110 |
|
|
set MainInit 1
|
111 |
|
|
set title Main
|
112 |
|
|
}
|
113 |
|
|
|
114 |
|
|
##
|
115 |
|
|
## When setting up all the default values, we always check for
|
116 |
|
|
## prior existence. This allows users who embed tkcon to modify
|
117 |
|
|
## the initial state before tkcon initializes itself.
|
118 |
|
|
##
|
119 |
|
|
|
120 |
|
|
# bg == {} will get bg color from the main toplevel (in InitUI)
|
121 |
|
|
# Modified by me, May 30, 2002 (modified colors).
|
122 |
|
|
foreach {key default} {
|
123 |
|
|
bg {black}
|
124 |
|
|
blink \#FFFF00
|
125 |
|
|
cursor grey
|
126 |
|
|
disabled \#4D4D4D
|
127 |
|
|
proc #7070FF
|
128 |
|
|
var \#FFC0D0
|
129 |
|
|
prompt #777777
|
130 |
|
|
stdin white
|
131 |
|
|
stdout yellow
|
132 |
|
|
stderr red
|
133 |
|
|
# stdin \#000000
|
134 |
|
|
# stdout \#0000FF
|
135 |
|
|
# stderr \#FF0000
|
136 |
|
|
} {
|
137 |
|
|
if {![info exists COLOR($key)]} { set COLOR($key) $default }
|
138 |
|
|
}
|
139 |
|
|
|
140 |
|
|
foreach {key default} {
|
141 |
|
|
autoload {}
|
142 |
|
|
blinktime 500
|
143 |
|
|
blinkrange 1
|
144 |
|
|
buffer 512
|
145 |
|
|
calcmode 0
|
146 |
|
|
cols 50
|
147 |
|
|
debugPrompt {(level \#$level) debug [history nextid] > }
|
148 |
|
|
dead {}
|
149 |
|
|
expandorder {Pathname Variable Procname}
|
150 |
|
|
font {Terminal 6}
|
151 |
|
|
history 48
|
152 |
|
|
hoterrors 1
|
153 |
|
|
library {}
|
154 |
|
|
lightbrace 1
|
155 |
|
|
lightcmd 1
|
156 |
|
|
maineval {}
|
157 |
|
|
maxmenu 15
|
158 |
|
|
nontcl 0
|
159 |
|
|
prompt1 {ignore this, it's set below}
|
160 |
|
|
rows 30
|
161 |
|
|
scrollypos right
|
162 |
|
|
showmenu 1
|
163 |
|
|
showmultiple 1
|
164 |
|
|
showstatusbar 0
|
165 |
|
|
slaveeval {}
|
166 |
|
|
slaveexit close
|
167 |
|
|
subhistory 1
|
168 |
|
|
gc-delay 60000
|
169 |
|
|
gets {congets}
|
170 |
|
|
usehistory 0
|
171 |
|
|
|
172 |
|
|
exec slave
|
173 |
|
|
} {
|
174 |
|
|
if {![info exists OPT($key)]} { set OPT($key) $default }
|
175 |
|
|
}
|
176 |
|
|
|
177 |
|
|
foreach {key default} {
|
178 |
|
|
app {}
|
179 |
|
|
appname {}
|
180 |
|
|
apptype slave
|
181 |
|
|
namesp ::
|
182 |
|
|
cmd {}
|
183 |
|
|
cmdbuf {}
|
184 |
|
|
cmdsave {}
|
185 |
|
|
event 1
|
186 |
|
|
deadapp 0
|
187 |
|
|
deadsock 0
|
188 |
|
|
debugging 0
|
189 |
|
|
displayWin .
|
190 |
|
|
histid 0
|
191 |
|
|
find {}
|
192 |
|
|
find,case 0
|
193 |
|
|
find,reg 0
|
194 |
|
|
errorInfo {}
|
195 |
|
|
showOnStartup 1
|
196 |
|
|
slavealias { edit more less tkcon }
|
197 |
|
|
slaveprocs {
|
198 |
|
|
alias clear dir dump echo idebug lremove
|
199 |
|
|
tkcon_puts tkcon_gets observe observe_var unalias which what
|
200 |
|
|
}
|
201 |
|
|
version 2.3
|
202 |
|
|
RCS {RCS: @(#) $Id: projman.tcl,v 1.1.1.1 2003-02-10 04:09:33 doru Exp $}
|
203 |
|
|
HEADURL {http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/tkcon/tkcon/tkcon.tcl?rev=HEAD}
|
204 |
|
|
docs "http://tkcon.sourceforge.net/"
|
205 |
|
|
email {jeff@hobbs.org}
|
206 |
|
|
root .
|
207 |
|
|
} {
|
208 |
|
|
if {![info exists PRIV($key)]} { set PRIV($key) $default }
|
209 |
|
|
}
|
210 |
|
|
|
211 |
|
|
## NOTES FOR STAYING IN PRIMARY INTERPRETER:
|
212 |
|
|
##
|
213 |
|
|
## If you set ::tkcon::OPT(exec) to {}, then instead of a multiple
|
214 |
|
|
## interp model, you get tkcon operating in the main interp by default.
|
215 |
|
|
## This can be useful when attaching to programs that like to operate
|
216 |
|
|
## in the main interpter (for example, based on special wish'es).
|
217 |
|
|
## You can set this from the command line with -exec ""
|
218 |
|
|
## A side effect is that all tkcon command line args will be used
|
219 |
|
|
## by the first console only.
|
220 |
|
|
#set OPT(exec) {}
|
221 |
|
|
|
222 |
|
|
if {$PRIV(WWW)} {
|
223 |
|
|
lappend PRIV(slavealias) history
|
224 |
|
|
set OPT(prompt1) {[history nextid] % }
|
225 |
|
|
} else {
|
226 |
|
|
lappend PRIV(slaveprocs) tcl_unknown unknown
|
227 |
|
|
set OPT(prompt1) {([file tail [pwd]]) [history nextid] % }
|
228 |
|
|
}
|
229 |
|
|
|
230 |
|
|
## If we are using the default '.' toplevel, and there appear to be
|
231 |
|
|
## children of '.', then make sure we use a disassociated toplevel.
|
232 |
|
|
if {$PRIV(root) == "." && [llength [winfo children .]]} {
|
233 |
|
|
set PRIV(root) .tkcon
|
234 |
|
|
}
|
235 |
|
|
|
236 |
|
|
## Do platform specific configuration here, other than defaults
|
237 |
|
|
### Use tkcon.cfg filename for resource filename on non-unix systems
|
238 |
|
|
### Determine what directory the resource file should be in
|
239 |
|
|
switch $tcl_platform(platform) {
|
240 |
|
|
macintosh {
|
241 |
|
|
if {![interp issafe]} {cd [file dirname [info script]]}
|
242 |
|
|
set envHome PREF_FOLDER
|
243 |
|
|
set rcfile tkcon.cfg
|
244 |
|
|
set histfile tkcon.hst
|
245 |
|
|
catch {console hide}
|
246 |
|
|
}
|
247 |
|
|
windows {
|
248 |
|
|
set envHome HOME
|
249 |
|
|
set rcfile tkcon.cfg
|
250 |
|
|
set histfile tkcon.hst
|
251 |
|
|
}
|
252 |
|
|
unix {
|
253 |
|
|
set envHome HOME
|
254 |
|
|
set rcfile .tkconrc
|
255 |
|
|
set histfile .tkcon_history
|
256 |
|
|
}
|
257 |
|
|
}
|
258 |
|
|
if {[info exists env($envHome)]} {
|
259 |
|
|
if {![info exists PRIV(rcfile)]} {
|
260 |
|
|
set PRIV(rcfile) [file join $env($envHome) $rcfile]
|
261 |
|
|
}
|
262 |
|
|
if {![info exists PRIV(histfile)]} {
|
263 |
|
|
set PRIV(histfile) [file join $env($envHome) $histfile]
|
264 |
|
|
}
|
265 |
|
|
}
|
266 |
|
|
|
267 |
|
|
## Handle command line arguments before sourcing resource file to
|
268 |
|
|
## find if resource file is being specified (let other args pass).
|
269 |
|
|
if {[set i [lsearch -exact $argv -rcfile]] != -1} {
|
270 |
|
|
set PRIV(rcfile) [lindex $argv [incr i]]
|
271 |
|
|
}
|
272 |
|
|
|
273 |
|
|
if {!$PRIV(WWW) && [file exists $PRIV(rcfile)]} {
|
274 |
|
|
set code [catch {uplevel \#0 [list source $PRIV(rcfile)]} err]
|
275 |
|
|
}
|
276 |
|
|
|
277 |
|
|
if {[info exists env(TK_CON_LIBRARY)]} {
|
278 |
|
|
lappend ::auto_path $env(TK_CON_LIBRARY)
|
279 |
|
|
} else {
|
280 |
|
|
lappend ::auto_path $OPT(library)
|
281 |
|
|
}
|
282 |
|
|
|
283 |
|
|
if {![info exists ::tcl_pkgPath]} {
|
284 |
|
|
set dir [file join [file dirname [info nameofexec]] lib]
|
285 |
|
|
if {[llength [info commands @scope]]} {
|
286 |
|
|
set dir [file join $dir itcl]
|
287 |
|
|
}
|
288 |
|
|
catch {source [file join $dir pkgIndex.tcl]}
|
289 |
|
|
}
|
290 |
|
|
catch {tclPkgUnknown dummy-name dummy-version}
|
291 |
|
|
|
292 |
|
|
## Handle rest of command line arguments after sourcing resource file
|
293 |
|
|
## and slave is created, but before initializing UI or setting packages.
|
294 |
|
|
set slaveargs {}
|
295 |
|
|
set slavefiles {}
|
296 |
|
|
set truth {^(1|yes|true|on)$}
|
297 |
|
|
for {set i 0} {$i < $argc} {incr i} {
|
298 |
|
|
set arg [lindex $argv $i]
|
299 |
|
|
if {[string match {-*} $arg]} {
|
300 |
|
|
set val [lindex $argv [incr i]]
|
301 |
|
|
## Handle arg based options
|
302 |
|
|
switch -glob -- $arg {
|
303 |
|
|
-- - -argv {
|
304 |
|
|
set argv [concat -- [lrange $argv $i end]]
|
305 |
|
|
set argc [llength $argv]
|
306 |
|
|
break
|
307 |
|
|
}
|
308 |
|
|
-color-* { set COLOR([string range $arg 7 end]) $val }
|
309 |
|
|
-exec { set OPT(exec) $val }
|
310 |
|
|
-main - -e - -eval { append OPT(maineval) \n$val\n }
|
311 |
|
|
-package - -load { lappend OPT(autoload) $val }
|
312 |
|
|
-slave { append OPT(slaveeval) \n$val\n }
|
313 |
|
|
-nontcl { set OPT(nontcl) [regexp -nocase $truth $val]}
|
314 |
|
|
-root { set PRIV(root) $val }
|
315 |
|
|
-font { set OPT(font) $val }
|
316 |
|
|
-rcfile {}
|
317 |
|
|
default { lappend slaveargs $arg; incr i -1 }
|
318 |
|
|
}
|
319 |
|
|
} elseif {[file isfile $arg]} {
|
320 |
|
|
lappend slavefiles $arg
|
321 |
|
|
} else {
|
322 |
|
|
lappend slaveargs $arg
|
323 |
|
|
}
|
324 |
|
|
}
|
325 |
|
|
|
326 |
|
|
## Create slave executable
|
327 |
|
|
if {[string compare {} $OPT(exec)]} {
|
328 |
|
|
uplevel \#0 ::tkcon::InitSlave $OPT(exec) $slaveargs
|
329 |
|
|
} else {
|
330 |
|
|
set argc [llength $slaveargs]
|
331 |
|
|
set argv $slaveargs
|
332 |
|
|
uplevel \#0 $slaveargs
|
333 |
|
|
}
|
334 |
|
|
|
335 |
|
|
## Attach to the slave, EvalAttached will then be effective
|
336 |
|
|
Attach $PRIV(appname) $PRIV(apptype)
|
337 |
|
|
InitUI $title
|
338 |
|
|
|
339 |
|
|
## swap puts and gets with the tkcon versions to make sure all
|
340 |
|
|
## input and output is handled by tkcon
|
341 |
|
|
if {![catch {rename ::puts ::tkcon_tcl_puts}]} {
|
342 |
|
|
interp alias {} ::puts {} ::tkcon_puts
|
343 |
|
|
}
|
344 |
|
|
if {($OPT(gets) != "") && ![catch {rename ::gets ::tkcon_tcl_gets}]} {
|
345 |
|
|
interp alias {} ::gets {} ::tkcon_gets
|
346 |
|
|
}
|
347 |
|
|
|
348 |
|
|
EvalSlave history keep $OPT(history)
|
349 |
|
|
if {[info exists MainInit]} {
|
350 |
|
|
# Source history file only for the main console, as all slave
|
351 |
|
|
# consoles will adopt from the main's history, but still
|
352 |
|
|
# keep separate histories
|
353 |
|
|
if {!$PRIV(WWW) && $OPT(usehistory) && [file exists $PRIV(histfile)]} {
|
354 |
|
|
# by me
|
355 |
|
|
#puts -nonewline "loading history file ... "
|
356 |
|
|
# The history file is built to be loaded in and
|
357 |
|
|
# understood by tkcon
|
358 |
|
|
if {[catch {uplevel \#0 [list source $PRIV(histfile)]} herr]} {
|
359 |
|
|
puts stderr "error:\n$herr"
|
360 |
|
|
append PRIV(errorInfo) $errorInfo\n
|
361 |
|
|
}
|
362 |
|
|
set PRIV(event) [EvalSlave history nextid]
|
363 |
|
|
# by me
|
364 |
|
|
#puts "[expr {$PRIV(event)-1}] events added"
|
365 |
|
|
}
|
366 |
|
|
}
|
367 |
|
|
|
368 |
|
|
## Autoload specified packages in slave
|
369 |
|
|
set pkgs [EvalSlave package names]
|
370 |
|
|
foreach pkg $OPT(autoload) {
|
371 |
|
|
puts -nonewline "autoloading package \"$pkg\" ... "
|
372 |
|
|
if {[lsearch -exact $pkgs $pkg]>-1} {
|
373 |
|
|
if {[catch {EvalSlave package require [list $pkg]} pkgerr]} {
|
374 |
|
|
puts stderr "error:\n$pkgerr"
|
375 |
|
|
append PRIV(errorInfo) $errorInfo\n
|
376 |
|
|
} else { puts "OK" }
|
377 |
|
|
} else {
|
378 |
|
|
puts stderr "error: package does not exist"
|
379 |
|
|
}
|
380 |
|
|
}
|
381 |
|
|
|
382 |
|
|
## Evaluate maineval in slave
|
383 |
|
|
if {[string compare {} $OPT(maineval)] && \
|
384 |
|
|
[catch {uplevel \#0 $OPT(maineval)} merr]} {
|
385 |
|
|
puts stderr "error in eval:\n$merr"
|
386 |
|
|
append PRIV(errorInfo) $errorInfo\n
|
387 |
|
|
}
|
388 |
|
|
|
389 |
|
|
## Source extra command line argument files into slave executable
|
390 |
|
|
foreach fn $slavefiles {
|
391 |
|
|
puts -nonewline "slave sourcing \"$fn\" ... "
|
392 |
|
|
if {[catch {EvalSlave source [list $fn]} fnerr]} {
|
393 |
|
|
puts stderr "error:\n$fnerr"
|
394 |
|
|
append PRIV(errorInfo) $errorInfo\n
|
395 |
|
|
} else { puts "OK" }
|
396 |
|
|
}
|
397 |
|
|
|
398 |
|
|
## Evaluate slaveeval in slave
|
399 |
|
|
if {[string compare {} $OPT(slaveeval)] && \
|
400 |
|
|
[catch {interp eval $OPT(exec) $OPT(slaveeval)} serr]} {
|
401 |
|
|
puts stderr "error in slave eval:\n$serr"
|
402 |
|
|
append PRIV(errorInfo) $errorInfo\n
|
403 |
|
|
}
|
404 |
|
|
## Output any error/output that may have been returned from rcfile
|
405 |
|
|
if {[info exists code] && $code && [string compare {} $err]} {
|
406 |
|
|
puts stderr "error in $PRIV(rcfile):\n$err"
|
407 |
|
|
append PRIV(errorInfo) $errorInfo
|
408 |
|
|
}
|
409 |
|
|
if {[string compare {} $OPT(exec)]} {
|
410 |
|
|
StateCheckpoint [concat $PRIV(name) $OPT(exec)] slave
|
411 |
|
|
}
|
412 |
|
|
StateCheckpoint $PRIV(name) slave
|
413 |
|
|
|
414 |
|
|
# by me
|
415 |
|
|
Prompt "$title console display active (Tcl$::tcl_patchLevel / Tk$::tk_patchLevel)\n"
|
416 |
|
|
}
|
417 |
|
|
|
418 |
|
|
## ::tkcon::InitSlave - inits the slave by placing key procs and aliases in it
|
419 |
|
|
## It's arg[cv] are based on passed in options, while argv0 is the same as
|
420 |
|
|
## the master. tcl_interactive is the same as the master as well.
|
421 |
|
|
# ARGS: slave - name of slave to init. If it does not exist, it is created.
|
422 |
|
|
# args - args to pass to a slave as argv/argc
|
423 |
|
|
##
|
424 |
|
|
proc ::tkcon::InitSlave {slave args} {
|
425 |
|
|
variable OPT
|
426 |
|
|
variable COLOR
|
427 |
|
|
variable PRIV
|
428 |
|
|
global argv0 tcl_interactive tcl_library env auto_path
|
429 |
|
|
|
430 |
|
|
if {[string match {} $slave]} {
|
431 |
|
|
return -code error "Don't init the master interpreter, goofball"
|
432 |
|
|
}
|
433 |
|
|
if {![interp exists $slave]} { interp create $slave }
|
434 |
|
|
if {[interp eval $slave info command source] == ""} {
|
435 |
|
|
$slave alias source SafeSource $slave
|
436 |
|
|
$slave alias load SafeLoad $slave
|
437 |
|
|
$slave alias open SafeOpen $slave
|
438 |
|
|
$slave alias file file
|
439 |
|
|
interp eval $slave [dump var -nocomplain tcl_library auto_path env]
|
440 |
|
|
interp eval $slave { catch {source [file join $tcl_library init.tcl]} }
|
441 |
|
|
interp eval $slave { catch unknown }
|
442 |
|
|
}
|
443 |
|
|
$slave alias exit exit
|
444 |
|
|
interp eval $slave {
|
445 |
|
|
# Do package require before changing around puts/gets
|
446 |
|
|
catch {package require bogus-package-name}
|
447 |
|
|
catch {rename ::puts ::tkcon_tcl_puts}
|
448 |
|
|
}
|
449 |
|
|
foreach cmd $PRIV(slaveprocs) { $slave eval [dump proc $cmd] }
|
450 |
|
|
foreach cmd $PRIV(slavealias) { $slave alias $cmd $cmd }
|
451 |
|
|
interp alias $slave ::ls $slave ::dir -full
|
452 |
|
|
interp alias $slave ::puts $slave ::tkcon_puts
|
453 |
|
|
if {$OPT(gets) != ""} {
|
454 |
|
|
interp eval $slave { catch {rename ::gets ::tkcon_tcl_gets} }
|
455 |
|
|
interp alias $slave ::gets $slave ::tkcon_gets
|
456 |
|
|
}
|
457 |
|
|
if {[info exists argv0]} {interp eval $slave [list set argv0 $argv0]}
|
458 |
|
|
interp eval $slave set tcl_interactive $tcl_interactive \; \
|
459 |
|
|
set auto_path [list $auto_path] \; \
|
460 |
|
|
set argc [llength $args] \; \
|
461 |
|
|
set argv [list $args] \; {
|
462 |
|
|
if {![llength [info command bgerror]]} {
|
463 |
|
|
proc bgerror err {
|
464 |
|
|
global errorInfo
|
465 |
|
|
set body [info body bgerror]
|
466 |
|
|
rename ::bgerror {}
|
467 |
|
|
if {[auto_load bgerror]} { return [bgerror $err] }
|
468 |
|
|
proc bgerror err $body
|
469 |
|
|
tkcon bgerror $err $errorInfo
|
470 |
|
|
}
|
471 |
|
|
}
|
472 |
|
|
}
|
473 |
|
|
|
474 |
|
|
foreach pkg [lremove [package names] Tcl] {
|
475 |
|
|
foreach v [package versions $pkg] {
|
476 |
|
|
interp eval $slave [list package ifneeded $pkg $v \
|
477 |
|
|
[package ifneeded $pkg $v]]
|
478 |
|
|
}
|
479 |
|
|
}
|
480 |
|
|
}
|
481 |
|
|
|
482 |
|
|
## ::tkcon::InitInterp - inits an interpreter by placing key
|
483 |
|
|
## procs and aliases in it.
|
484 |
|
|
# ARGS: name - interp name
|
485 |
|
|
# type - interp type (slave|interp)
|
486 |
|
|
##
|
487 |
|
|
proc ::tkcon::InitInterp {name type} {
|
488 |
|
|
variable OPT
|
489 |
|
|
variable PRIV
|
490 |
|
|
|
491 |
|
|
## Don't allow messing up a local master interpreter
|
492 |
|
|
if {[string match namespace $type] || ([string match slave $type] && \
|
493 |
|
|
[regexp {^([Mm]ain|Slave[0-9]+)$} $name])} return
|
494 |
|
|
set old [Attach]
|
495 |
|
|
set oldname $PRIV(namesp)
|
496 |
|
|
catch {
|
497 |
|
|
Attach $name $type
|
498 |
|
|
EvalAttached { catch {rename ::puts ::tkcon_tcl_puts} }
|
499 |
|
|
foreach cmd $PRIV(slaveprocs) { EvalAttached [dump proc $cmd] }
|
500 |
|
|
switch -exact $type {
|
501 |
|
|
slave {
|
502 |
|
|
foreach cmd $PRIV(slavealias) {
|
503 |
|
|
Main interp alias $name ::$cmd $PRIV(name) ::$cmd
|
504 |
|
|
}
|
505 |
|
|
}
|
506 |
|
|
interp {
|
507 |
|
|
set thistkcon [tk appname]
|
508 |
|
|
foreach cmd $PRIV(slavealias) {
|
509 |
|
|
EvalAttached "proc $cmd args { send [list $thistkcon] $cmd \$args }"
|
510 |
|
|
}
|
511 |
|
|
}
|
512 |
|
|
}
|
513 |
|
|
## Catch in case it's a 7.4 (no 'interp alias') interp
|
514 |
|
|
EvalAttached {
|
515 |
|
|
catch {interp alias {} ::ls {} ::dir -full}
|
516 |
|
|
if {[catch {interp alias {} ::puts {} ::tkcon_puts}]} {
|
517 |
|
|
catch {rename ::tkcon_puts ::puts}
|
518 |
|
|
}
|
519 |
|
|
}
|
520 |
|
|
if {$OPT(gets) != ""} {
|
521 |
|
|
EvalAttached {
|
522 |
|
|
catch {rename ::gets ::tkcon_tcl_gets}
|
523 |
|
|
if {[catch {interp alias {} ::gets {} ::tkcon_gets}]} {
|
524 |
|
|
catch {rename ::tkcon_gets ::gets}
|
525 |
|
|
}
|
526 |
|
|
}
|
527 |
|
|
}
|
528 |
|
|
return
|
529 |
|
|
} {err}
|
530 |
|
|
eval Attach $old
|
531 |
|
|
AttachNamespace $oldname
|
532 |
|
|
if {[string compare {} $err]} { return -code error $err }
|
533 |
|
|
}
|
534 |
|
|
|
535 |
|
|
|
536 |
|
|
|
537 |
|
|
## ::tkcon::InitUI - inits UI portion (console) of tkcon
|
538 |
|
|
## Creates all elements of the console window and sets up the text tags
|
539 |
|
|
# ARGS: root - widget pathname of the tkcon console root
|
540 |
|
|
# title - title for the console root and main (.) windows
|
541 |
|
|
# Calls: ::tkcon::InitMenus, ::tkcon::Prompt
|
542 |
|
|
##
|
543 |
|
|
proc ::tkcon::InitUI {title} {
|
544 |
|
|
variable OPT
|
545 |
|
|
variable PRIV
|
546 |
|
|
variable COLOR
|
547 |
|
|
|
548 |
|
|
set root $PRIV(root)
|
549 |
|
|
if {[string match . $root]} { set w {} } else { set w [toplevel $root] }
|
550 |
|
|
|
551 |
|
|
# by me
|
552 |
|
|
frame $w.bf
|
553 |
|
|
pack $w.bf -fill x -side top
|
554 |
|
|
button $w.bf.ed -text Edit -command ::tkcon::CmdEd
|
555 |
|
|
button $w.bf.cpl -text Compile -command ::tkcon::CmdCpl
|
556 |
|
|
button $w.bf.run -text Run -command ::tkcon::CmdRun
|
557 |
|
|
button $w.bf.clp -text "Clean project" -command ::tkcon::CmdClp
|
558 |
|
|
button $w.bf.clc -text "Clean console" -command ::tkcon::CmdClc
|
559 |
|
|
pack $w.bf.ed -side left
|
560 |
|
|
pack $w.bf.cpl -side left
|
561 |
|
|
pack $w.bf.run -side left
|
562 |
|
|
pack $w.bf.clp -side left
|
563 |
|
|
pack $w.bf.clc -side left
|
564 |
|
|
|
565 |
|
|
if {!$PRIV(WWW)} {
|
566 |
|
|
wm withdraw $root
|
567 |
|
|
wm protocol $root WM_DELETE_WINDOW exit
|
568 |
|
|
}
|
569 |
|
|
set PRIV(base) $w
|
570 |
|
|
|
571 |
|
|
## Text Console
|
572 |
|
|
set PRIV(console) [set con $w.text]
|
573 |
|
|
text $con -wrap char -yscrollcommand [list $w.sy set] \
|
574 |
|
|
-foreground $COLOR(stdin) \
|
575 |
|
|
-insertbackground $COLOR(cursor)
|
576 |
|
|
$con mark set output 1.0
|
577 |
|
|
$con mark set limit 1.0
|
578 |
|
|
if {[string compare {} $COLOR(bg)]} {
|
579 |
|
|
$con configure -background $COLOR(bg)
|
580 |
|
|
}
|
581 |
|
|
set COLOR(bg) [$con cget -background]
|
582 |
|
|
if {[string compare {} $OPT(font)]} {
|
583 |
|
|
## Set user-requested font, if any
|
584 |
|
|
$con configure -font $OPT(font)
|
585 |
|
|
} else {
|
586 |
|
|
## otherwise make sure the font is monospace
|
587 |
|
|
set font [$con cget -font]
|
588 |
|
|
if {![font metrics $font -fixed]} {
|
589 |
|
|
font create tkconfixed -family Courier -size 12
|
590 |
|
|
$con configure -font tkconfixed
|
591 |
|
|
}
|
592 |
|
|
}
|
593 |
|
|
set OPT(font) [$con cget -font]
|
594 |
|
|
if {!$PRIV(WWW)} {
|
595 |
|
|
$con configure -setgrid 1 -width $OPT(cols) -height $OPT(rows)
|
596 |
|
|
}
|
597 |
|
|
bindtags $con [list $con TkConsole TkConsolePost $root all]
|
598 |
|
|
## Menus
|
599 |
|
|
## catch against use in plugin
|
600 |
|
|
if {[catch {menu $w.mbar} PRIV(menubar)]} {
|
601 |
|
|
set PRIV(menubar) [frame $w.mbar -relief raised -bd 1]
|
602 |
|
|
}
|
603 |
|
|
## Scrollbar
|
604 |
|
|
set PRIV(scrolly) [scrollbar $w.sy -takefocus 0 -bd 1 \
|
605 |
|
|
-command [list $con yview]]
|
606 |
|
|
|
607 |
|
|
# Modified by me, May 30, 2002 (removed menus).
|
608 |
|
|
# InitMenus $PRIV(menubar) $title
|
609 |
|
|
Bindings
|
610 |
|
|
|
611 |
|
|
if {$OPT(showmenu)} {
|
612 |
|
|
$root configure -menu $PRIV(menubar)
|
613 |
|
|
}
|
614 |
|
|
pack $w.sy -side $OPT(scrollypos) -fill y
|
615 |
|
|
pack $con -fill both -expand 1
|
616 |
|
|
|
617 |
|
|
set PRIV(statusbar) [set sbar [frame $w.sbar]]
|
618 |
|
|
label $sbar.attach -relief sunken -bd 1 -anchor w \
|
619 |
|
|
-textvariable ::tkcon::PRIV(StatusAttach)
|
620 |
|
|
label $sbar.mode -relief sunken -bd 1 -anchor w \
|
621 |
|
|
-textvariable ::tkcon::PRIV(StatusMode)
|
622 |
|
|
label $sbar.cursor -relief sunken -bd 1 -anchor w -width 6 \
|
623 |
|
|
-textvariable ::tkcon::PRIV(StatusCursor)
|
624 |
|
|
grid $sbar.attach $sbar.mode $sbar.cursor -sticky news -padx 1
|
625 |
|
|
grid columnconfigure $sbar 0 -weight 1
|
626 |
|
|
grid columnconfigure $sbar 1 -weight 1
|
627 |
|
|
grid columnconfigure $sbar 2 -weight 0
|
628 |
|
|
|
629 |
|
|
if {$OPT(showstatusbar)} {
|
630 |
|
|
pack $sbar -side bottom -fill x -before $::tkcon::PRIV(scrolly)
|
631 |
|
|
}
|
632 |
|
|
|
633 |
|
|
foreach col {prompt stdout stderr stdin proc} {
|
634 |
|
|
$con tag configure $col -foreground $COLOR($col)
|
635 |
|
|
}
|
636 |
|
|
$con tag configure var -background $COLOR(var)
|
637 |
|
|
$con tag raise sel
|
638 |
|
|
$con tag configure blink -background $COLOR(blink)
|
639 |
|
|
$con tag configure find -background $COLOR(blink)
|
640 |
|
|
|
641 |
|
|
if {!$PRIV(WWW)} {
|
642 |
|
|
# by me
|
643 |
|
|
#wm title $root "tkcon $PRIV(version) $title"
|
644 |
|
|
bind $con <Configure> {
|
645 |
|
|
scan [wm geometry [winfo toplevel %W]] "%%dx%%d" \
|
646 |
|
|
::tkcon::OPT(cols) ::tkcon::OPT(rows)
|
647 |
|
|
}
|
648 |
|
|
if {$PRIV(showOnStartup)} { wm deiconify $root }
|
649 |
|
|
}
|
650 |
|
|
if {$PRIV(showOnStartup)} { focus -force $PRIV(console) }
|
651 |
|
|
if {$OPT(gc-delay)} {
|
652 |
|
|
after $OPT(gc-delay) ::tkcon::GarbageCollect
|
653 |
|
|
}
|
654 |
|
|
}
|
655 |
|
|
|
656 |
|
|
# by me
|
657 |
|
|
proc ::tkcon::CmdEd {} {
|
658 |
|
|
variable PRIV
|
659 |
|
|
EvalExt $PRIV(console) "edit.bat"
|
660 |
|
|
}
|
661 |
|
|
proc ::tkcon::CmdCpl {} {
|
662 |
|
|
variable PRIV
|
663 |
|
|
EvalExt $PRIV(console) "compile.bat"
|
664 |
|
|
}
|
665 |
|
|
|
666 |
|
|
proc ::tkcon::CmdRun {} {
|
667 |
|
|
variable PRIV
|
668 |
|
|
EvalExt $PRIV(console) "run.bat"
|
669 |
|
|
}
|
670 |
|
|
|
671 |
|
|
proc ::tkcon::CmdClp {} {
|
672 |
|
|
variable PRIV
|
673 |
|
|
EvalExt $PRIV(console) "clean.bat"
|
674 |
|
|
}
|
675 |
|
|
|
676 |
|
|
proc ::tkcon::CmdClc {} {
|
677 |
|
|
variable PRIV
|
678 |
|
|
$PRIV(console) delete 0.0 end
|
679 |
|
|
}
|
680 |
|
|
|
681 |
|
|
|
682 |
|
|
|
683 |
|
|
|
684 |
|
|
|
685 |
|
|
|
686 |
|
|
|
687 |
|
|
## ::tkcon::GarbageCollect - do various cleanup ops periodically to our setup
|
688 |
|
|
##
|
689 |
|
|
proc ::tkcon::GarbageCollect {} {
|
690 |
|
|
variable OPT
|
691 |
|
|
variable PRIV
|
692 |
|
|
|
693 |
|
|
set w $PRIV(console)
|
694 |
|
|
## Remove error tags that no longer span anything
|
695 |
|
|
## Make sure the tag pattern matches the unique tag prefix
|
696 |
|
|
foreach tag [$w tag names] {
|
697 |
|
|
if {[string match _tag* $tag] && ![llength [$w tag ranges $tag]]} {
|
698 |
|
|
$w tag delete $tag
|
699 |
|
|
}
|
700 |
|
|
}
|
701 |
|
|
if {$OPT(gc-delay)} {
|
702 |
|
|
after $OPT(gc-delay) ::tkcon::GarbageCollect
|
703 |
|
|
}
|
704 |
|
|
}
|
705 |
|
|
|
706 |
|
|
## ::tkcon::Eval - evaluates commands input into console window
|
707 |
|
|
## This is the first stage of the evaluating commands in the console.
|
708 |
|
|
## They need to be broken up into consituent commands (by ::tkcon::CmdSep) in
|
709 |
|
|
## case a multiple commands were pasted in, then each is eval'ed (by
|
710 |
|
|
## ::tkcon::EvalCmd) in turn. Any uncompleted command will not be eval'ed.
|
711 |
|
|
# ARGS: w - console text widget
|
712 |
|
|
# Calls: ::tkcon::CmdGet, ::tkcon::CmdSep, ::tkcon::EvalCmd
|
713 |
|
|
##
|
714 |
|
|
proc ::tkcon::Eval {w} {
|
715 |
|
|
set incomplete [CmdSep [CmdGet $w] cmds last]
|
716 |
|
|
$w mark set insert end-1c
|
717 |
|
|
$w insert end \n
|
718 |
|
|
if {[llength $cmds]} {
|
719 |
|
|
foreach c $cmds {EvalCmd $w $c}
|
720 |
|
|
$w insert insert $last {}
|
721 |
|
|
} elseif {!$incomplete} {
|
722 |
|
|
EvalCmd $w $last
|
723 |
|
|
}
|
724 |
|
|
$w see insert
|
725 |
|
|
}
|
726 |
|
|
|
727 |
|
|
# by me
|
728 |
|
|
proc ::tkcon::EvalExt {w cc} {
|
729 |
|
|
set incomplete [CmdSep $cc cmds last]
|
730 |
|
|
$w mark set insert end-1c
|
731 |
|
|
$w insert end \n
|
732 |
|
|
if {[llength $cmds]} {
|
733 |
|
|
foreach c $cmds {EvalCmd $w $c}
|
734 |
|
|
$w insert insert $last {}
|
735 |
|
|
} elseif {!$incomplete} {
|
736 |
|
|
EvalCmd $w $last
|
737 |
|
|
}
|
738 |
|
|
$w see insert
|
739 |
|
|
}
|
740 |
|
|
|
741 |
|
|
## ::tkcon::EvalCmd - evaluates a single command, adding it to history
|
742 |
|
|
# ARGS: w - console text widget
|
743 |
|
|
# cmd - the command to evaluate
|
744 |
|
|
# Calls: ::tkcon::Prompt
|
745 |
|
|
# Outputs: result of command to stdout (or stderr if error occured)
|
746 |
|
|
# Returns: next event number
|
747 |
|
|
##
|
748 |
|
|
proc ::tkcon::EvalCmd {w cmd} {
|
749 |
|
|
variable OPT
|
750 |
|
|
variable PRIV
|
751 |
|
|
|
752 |
|
|
$w mark set output end
|
753 |
|
|
if {[string compare {} $cmd]} {
|
754 |
|
|
set code 0
|
755 |
|
|
if {$OPT(subhistory)} {
|
756 |
|
|
set ev [EvalSlave history nextid]
|
757 |
|
|
incr ev -1
|
758 |
|
|
if {[string match !! $cmd]} {
|
759 |
|
|
set code [catch {EvalSlave history event $ev} cmd]
|
760 |
|
|
if {!$code} {$w insert output $cmd\n stdin}
|
761 |
|
|
} elseif {[regexp {^!(.+)$} $cmd dummy event]} {
|
762 |
|
|
## Check last event because history event is broken
|
763 |
|
|
set code [catch {EvalSlave history event $ev} cmd]
|
764 |
|
|
if {!$code && ![string match ${event}* $cmd]} {
|
765 |
|
|
set code [catch {EvalSlave history event $event} cmd]
|
766 |
|
|
}
|
767 |
|
|
if {!$code} {$w insert output $cmd\n stdin}
|
768 |
|
|
} elseif {[regexp {^\^([^^]*)\^([^^]*)\^?$} $cmd dummy old new]} {
|
769 |
|
|
set code [catch {EvalSlave history event $ev} cmd]
|
770 |
|
|
if {!$code} {
|
771 |
|
|
regsub -all -- $old $cmd $new cmd
|
772 |
|
|
$w insert output $cmd\n stdin
|
773 |
|
|
}
|
774 |
|
|
} elseif {$OPT(calcmode) && ![catch {expr $cmd} err]} {
|
775 |
|
|
EvalSlave history add $cmd
|
776 |
|
|
set cmd $err
|
777 |
|
|
set code -1
|
778 |
|
|
}
|
779 |
|
|
}
|
780 |
|
|
if {$code} {
|
781 |
|
|
$w insert output $cmd\n stderr
|
782 |
|
|
} else {
|
783 |
|
|
## We are about to evaluate the command, so move the limit
|
784 |
|
|
## mark to ensure that further <Return>s don't cause double
|
785 |
|
|
## evaluation of this command - for cases like the command
|
786 |
|
|
## has a vwait or something in it
|
787 |
|
|
$w mark set limit end
|
788 |
|
|
if {$OPT(nontcl) && [string match interp $PRIV(apptype)]} {
|
789 |
|
|
set code [catch {EvalSend $cmd} res]
|
790 |
|
|
if {$code == 1} {
|
791 |
|
|
set PRIV(errorInfo) "Non-Tcl errorInfo not available"
|
792 |
|
|
}
|
793 |
|
|
} elseif {[string match socket $PRIV(apptype)]} {
|
794 |
|
|
set code [catch {EvalSocket $cmd} res]
|
795 |
|
|
if {$code == 1} {
|
796 |
|
|
set PRIV(errorInfo) "Socket-based errorInfo not available"
|
797 |
|
|
}
|
798 |
|
|
} else {
|
799 |
|
|
set code [catch {EvalAttached $cmd} res]
|
800 |
|
|
if {$code == 1} {
|
801 |
|
|
if {[catch {EvalAttached [list set errorInfo]} err]} {
|
802 |
|
|
set PRIV(errorInfo) "Error getting errorInfo:\n$err"
|
803 |
|
|
} else {
|
804 |
|
|
set PRIV(errorInfo) $err
|
805 |
|
|
}
|
806 |
|
|
}
|
807 |
|
|
}
|
808 |
|
|
EvalSlave history add $cmd
|
809 |
|
|
if {$code} {
|
810 |
|
|
if {$OPT(hoterrors)} {
|
811 |
|
|
set tag [UniqueTag $w]
|
812 |
|
|
$w insert output $res [list stderr $tag] \n stderr
|
813 |
|
|
$w tag bind $tag <Enter> \
|
814 |
|
|
[list $w tag configure $tag -under 1]
|
815 |
|
|
$w tag bind $tag <Leave> \
|
816 |
|
|
[list $w tag configure $tag -under 0]
|
817 |
|
|
$w tag bind $tag <ButtonRelease-1> \
|
818 |
|
|
"if {!\[info exists tkPriv(mouseMoved)\] || !\$tkPriv(mouseMoved)} \
|
819 |
|
|
{[list edit -attach [Attach] -type error -- $PRIV(errorInfo)]}"
|
820 |
|
|
} else {
|
821 |
|
|
$w insert output $res\n stderr
|
822 |
|
|
}
|
823 |
|
|
} elseif {[string compare {} $res]} {
|
824 |
|
|
$w insert output $res\n stdout
|
825 |
|
|
}
|
826 |
|
|
}
|
827 |
|
|
}
|
828 |
|
|
Prompt
|
829 |
|
|
set PRIV(event) [EvalSlave history nextid]
|
830 |
|
|
}
|
831 |
|
|
|
832 |
|
|
## ::tkcon::EvalSlave - evaluates the args in the associated slave
|
833 |
|
|
## args should be passed to this procedure like they would be at
|
834 |
|
|
## the command line (not like to 'eval').
|
835 |
|
|
# ARGS: args - the command and args to evaluate
|
836 |
|
|
##
|
837 |
|
|
proc ::tkcon::EvalSlave args {
|
838 |
|
|
interp eval $::tkcon::OPT(exec) $args
|
839 |
|
|
}
|
840 |
|
|
|
841 |
|
|
## ::tkcon::EvalOther - evaluate a command in a foreign interp or slave
|
842 |
|
|
## without attaching to it. No check for existence is made.
|
843 |
|
|
# ARGS: app - interp/slave name
|
844 |
|
|
# type - (slave|interp)
|
845 |
|
|
##
|
846 |
|
|
proc ::tkcon::EvalOther { app type args } {
|
847 |
|
|
if {[string compare slave $type]==0} {
|
848 |
|
|
return [Slave $app $args]
|
849 |
|
|
} else {
|
850 |
|
|
return [uplevel 1 send [list $app] $args]
|
851 |
|
|
}
|
852 |
|
|
}
|
853 |
|
|
|
854 |
|
|
## ::tkcon::EvalSend - sends the args to the attached interpreter
|
855 |
|
|
## Varies from 'send' by determining whether attachment is dead
|
856 |
|
|
## when an error is received
|
857 |
|
|
# ARGS: cmd - the command string to send across
|
858 |
|
|
# Returns: the result of the command
|
859 |
|
|
##
|
860 |
|
|
proc ::tkcon::EvalSend cmd {
|
861 |
|
|
variable OPT
|
862 |
|
|
variable PRIV
|
863 |
|
|
|
864 |
|
|
if {$PRIV(deadapp)} {
|
865 |
|
|
if {[lsearch -exact [winfo interps] $PRIV(app)]<0} {
|
866 |
|
|
return
|
867 |
|
|
} else {
|
868 |
|
|
set PRIV(appname) [string range $PRIV(appname) 5 end]
|
869 |
|
|
set PRIV(deadapp) 0
|
870 |
|
|
Prompt "\n\"$PRIV(app)\" alive\n" [CmdGet $PRIV(console)]
|
871 |
|
|
}
|
872 |
|
|
}
|
873 |
|
|
set code [catch {send -displayof $PRIV(displayWin) $PRIV(app) $cmd} result]
|
874 |
|
|
if {$code && [lsearch -exact [winfo interps] $PRIV(app)]<0} {
|
875 |
|
|
## Interpreter disappeared
|
876 |
|
|
if {[string compare leave $OPT(dead)] && \
|
877 |
|
|
([string match ignore $OPT(dead)] || \
|
878 |
|
|
[tk_dialog $PRIV(base).dead "Dead Attachment" \
|
879 |
|
|
"\"$PRIV(app)\" appears to have died.\
|
880 |
|
|
\nReturn to primary slave interpreter?" questhead 0 OK No])} {
|
881 |
|
|
set PRIV(appname) "DEAD:$PRIV(appname)"
|
882 |
|
|
set PRIV(deadapp) 1
|
883 |
|
|
} else {
|
884 |
|
|
set err "Attached Tk interpreter \"$PRIV(app)\" died."
|
885 |
|
|
Attach {}
|
886 |
|
|
set PRIV(deadapp) 0
|
887 |
|
|
EvalSlave set errorInfo $err
|
888 |
|
|
}
|
889 |
|
|
Prompt \n [CmdGet $PRIV(console)]
|
890 |
|
|
}
|
891 |
|
|
return -code $code $result
|
892 |
|
|
}
|
893 |
|
|
|
894 |
|
|
## ::tkcon::EvalSocket - sends the string to an interpreter attached via
|
895 |
|
|
## a tcp/ip socket
|
896 |
|
|
##
|
897 |
|
|
## In the EvalSocket case, ::tkcon::PRIV(app) is the socket id
|
898 |
|
|
##
|
899 |
|
|
## Must determine whether socket is dead when an error is received
|
900 |
|
|
# ARGS: cmd - the data string to send across
|
901 |
|
|
# Returns: the result of the command
|
902 |
|
|
##
|
903 |
|
|
proc ::tkcon::EvalSocket cmd {
|
904 |
|
|
variable OPT
|
905 |
|
|
variable PRIV
|
906 |
|
|
global tcl_version
|
907 |
|
|
|
908 |
|
|
if {$PRIV(deadapp)} {
|
909 |
|
|
if {![info exists PRIV(app)] || \
|
910 |
|
|
[catch {eof $PRIV(app)} eof] || $eof} {
|
911 |
|
|
return
|
912 |
|
|
} else {
|
913 |
|
|
set PRIV(appname) [string range $PRIV(appname) 5 end]
|
914 |
|
|
set PRIV(deadapp) 0
|
915 |
|
|
Prompt "\n\"$PRIV(app)\" alive\n" [CmdGet $PRIV(console)]
|
916 |
|
|
}
|
917 |
|
|
}
|
918 |
|
|
# Sockets get \'s interpreted, so that users can
|
919 |
|
|
# send things like \n\r or explicit hex values
|
920 |
|
|
set cmd [subst -novariables -nocommands $cmd]
|
921 |
|
|
#puts [list $PRIV(app) $cmd]
|
922 |
|
|
set code [catch {puts $PRIV(app) $cmd ; flush $PRIV(app)} result]
|
923 |
|
|
if {$code && [eof $PRIV(app)]} {
|
924 |
|
|
## Interpreter died or disappeared
|
925 |
|
|
puts "$code eof [eof $PRIV(app)]"
|
926 |
|
|
EvalSocketClosed
|
927 |
|
|
}
|
928 |
|
|
return -code $code $result
|
929 |
|
|
}
|
930 |
|
|
|
931 |
|
|
## ::tkcon::EvalSocketEvent - fileevent command for an interpreter attached
|
932 |
|
|
## via a tcp/ip socket
|
933 |
|
|
## Must determine whether socket is dead when an error is received
|
934 |
|
|
# ARGS: args - the args to send across
|
935 |
|
|
# Returns: the result of the command
|
936 |
|
|
##
|
937 |
|
|
proc ::tkcon::EvalSocketEvent {} {
|
938 |
|
|
variable PRIV
|
939 |
|
|
|
940 |
|
|
if {[gets $PRIV(app) line] == -1} {
|
941 |
|
|
if {[eof $PRIV(app)]} {
|
942 |
|
|
EvalSocketClosed
|
943 |
|
|
}
|
944 |
|
|
return
|
945 |
|
|
}
|
946 |
|
|
puts $line
|
947 |
|
|
}
|
948 |
|
|
|
949 |
|
|
## ::tkcon::EvalSocketClosed - takes care of handling a closed eval socket
|
950 |
|
|
##
|
951 |
|
|
# ARGS: args - the args to send across
|
952 |
|
|
# Returns: the result of the command
|
953 |
|
|
##
|
954 |
|
|
proc ::tkcon::EvalSocketClosed {} {
|
955 |
|
|
variable OPT
|
956 |
|
|
variable PRIV
|
957 |
|
|
|
958 |
|
|
catch {close $PRIV(app)}
|
959 |
|
|
if {[string compare leave $OPT(dead)] && \
|
960 |
|
|
([string match ignore $OPT(dead)] || \
|
961 |
|
|
[tk_dialog $PRIV(base).dead "Dead Attachment" \
|
962 |
|
|
"\"$PRIV(app)\" appears to have died.\
|
963 |
|
|
\nReturn to primary slave interpreter?" questhead 0 OK No])} {
|
964 |
|
|
set PRIV(appname) "DEAD:$PRIV(appname)"
|
965 |
|
|
set PRIV(deadapp) 1
|
966 |
|
|
} else {
|
967 |
|
|
set err "Attached Tk interpreter \"$PRIV(app)\" died."
|
968 |
|
|
Attach {}
|
969 |
|
|
set PRIV(deadapp) 0
|
970 |
|
|
EvalSlave set errorInfo $err
|
971 |
|
|
}
|
972 |
|
|
Prompt \n [CmdGet $PRIV(console)]
|
973 |
|
|
}
|
974 |
|
|
|
975 |
|
|
## ::tkcon::EvalNamespace - evaluates the args in a particular namespace
|
976 |
|
|
## This is an override for ::tkcon::EvalAttached for when the user wants
|
977 |
|
|
## to attach to a particular namespace of the attached interp
|
978 |
|
|
# ARGS: attached
|
979 |
|
|
# namespace the namespace to evaluate in
|
980 |
|
|
# args the args to evaluate
|
981 |
|
|
# RETURNS: the result of the command
|
982 |
|
|
##
|
983 |
|
|
proc ::tkcon::EvalNamespace { attached namespace args } {
|
984 |
|
|
if {[llength $args]} {
|
985 |
|
|
uplevel \#0 $attached \
|
986 |
|
|
[list [concat [list namespace eval $namespace] $args]]
|
987 |
|
|
}
|
988 |
|
|
}
|
989 |
|
|
|
990 |
|
|
|
991 |
|
|
## ::tkcon::Namespaces - return all the namespaces descendent from $ns
|
992 |
|
|
##
|
993 |
|
|
#
|
994 |
|
|
##
|
995 |
|
|
proc ::tkcon::Namespaces {{ns ::} {l {}}} {
|
996 |
|
|
if {[string compare {} $ns]} { lappend l $ns }
|
997 |
|
|
foreach i [EvalAttached [list namespace children $ns]] {
|
998 |
|
|
set l [Namespaces $i $l]
|
999 |
|
|
}
|
1000 |
|
|
return $l
|
1001 |
|
|
}
|
1002 |
|
|
|
1003 |
|
|
## ::tkcon::CmdGet - gets the current command from the console widget
|
1004 |
|
|
# ARGS: w - console text widget
|
1005 |
|
|
# Returns: text which compromises current command line
|
1006 |
|
|
##
|
1007 |
|
|
proc ::tkcon::CmdGet w {
|
1008 |
|
|
if {![llength [$w tag nextrange prompt limit end]]} {
|
1009 |
|
|
$w tag add stdin limit end-1c
|
1010 |
|
|
return [$w get limit end-1c]
|
1011 |
|
|
}
|
1012 |
|
|
}
|
1013 |
|
|
|
1014 |
|
|
## ::tkcon::CmdSep - separates multiple commands into a list and remainder
|
1015 |
|
|
# ARGS: cmd - (possible) multiple command to separate
|
1016 |
|
|
# list - varname for the list of commands that were separated.
|
1017 |
|
|
# last - varname of any remainder (like an incomplete final command).
|
1018 |
|
|
# If there is only one command, it's placed in this var.
|
1019 |
|
|
# Returns: constituent command info in varnames specified by list & rmd.
|
1020 |
|
|
##
|
1021 |
|
|
proc ::tkcon::CmdSep {cmd list last} {
|
1022 |
|
|
upvar 1 $list cmds $last inc
|
1023 |
|
|
set inc {}
|
1024 |
|
|
set cmds {}
|
1025 |
|
|
foreach c [split [string trimleft $cmd] \n] {
|
1026 |
|
|
if {[string compare $inc {}]} {
|
1027 |
|
|
append inc \n$c
|
1028 |
|
|
} else {
|
1029 |
|
|
append inc [string trimleft $c]
|
1030 |
|
|
}
|
1031 |
|
|
if {[info complete $inc] && ![regexp {[^\\]\\$} $inc]} {
|
1032 |
|
|
if {[regexp "^\[^#\]" $inc]} {lappend cmds $inc}
|
1033 |
|
|
set inc {}
|
1034 |
|
|
}
|
1035 |
|
|
}
|
1036 |
|
|
set i [string compare $inc {}]
|
1037 |
|
|
if {!$i && [string compare $cmds {}] && ![string match *\n $cmd]} {
|
1038 |
|
|
set inc [lindex $cmds end]
|
1039 |
|
|
set cmds [lreplace $cmds end end]
|
1040 |
|
|
}
|
1041 |
|
|
return $i
|
1042 |
|
|
}
|
1043 |
|
|
|
1044 |
|
|
## ::tkcon::CmdSplit - splits multiple commands into a list
|
1045 |
|
|
# ARGS: cmd - (possible) multiple command to separate
|
1046 |
|
|
# Returns: constituent commands in a list
|
1047 |
|
|
##
|
1048 |
|
|
proc ::tkcon::CmdSplit {cmd} {
|
1049 |
|
|
set inc {}
|
1050 |
|
|
set cmds {}
|
1051 |
|
|
foreach cmd [split [string trimleft $cmd] \n] {
|
1052 |
|
|
if {[string compare {} $inc]} {
|
1053 |
|
|
append inc \n$cmd
|
1054 |
|
|
} else {
|
1055 |
|
|
append inc [string trimleft $cmd]
|
1056 |
|
|
}
|
1057 |
|
|
if {[info complete $inc] && ![regexp {[^\\]\\$} $inc]} {
|
1058 |
|
|
#set inc [string trimright $inc]
|
1059 |
|
|
if {[regexp "^\[^#\]" $inc]} {lappend cmds $inc}
|
1060 |
|
|
set inc {}
|
1061 |
|
|
}
|
1062 |
|
|
}
|
1063 |
|
|
if {[regexp "^\[^#\]" $inc]} {lappend cmds $inc}
|
1064 |
|
|
return $cmds
|
1065 |
|
|
}
|
1066 |
|
|
|
1067 |
|
|
## ::tkcon::UniqueTag - creates a uniquely named tag, reusing names
|
1068 |
|
|
## Called by ::tkcon::EvalCmd
|
1069 |
|
|
# ARGS: w - text widget
|
1070 |
|
|
# Outputs: tag name guaranteed unique in the widget
|
1071 |
|
|
##
|
1072 |
|
|
proc ::tkcon::UniqueTag {w} {
|
1073 |
|
|
set tags [$w tag names]
|
1074 |
|
|
set idx 0
|
1075 |
|
|
while {[lsearch -exact $tags _tag[incr idx]] != -1} {}
|
1076 |
|
|
return _tag$idx
|
1077 |
|
|
}
|
1078 |
|
|
|
1079 |
|
|
## ::tkcon::ConstrainBuffer - This limits the amount of data in the text widget
|
1080 |
|
|
## Called by ::tkcon::Prompt and in tkcon proc buffer/console switch cases
|
1081 |
|
|
# ARGS: w - console text widget
|
1082 |
|
|
# size - # of lines to constrain to
|
1083 |
|
|
# Outputs: may delete data in console widget
|
1084 |
|
|
##
|
1085 |
|
|
proc ::tkcon::ConstrainBuffer {w size} {
|
1086 |
|
|
if {[$w index end] > $size} {
|
1087 |
|
|
$w delete 1.0 [expr {int([$w index end])-$size}].0
|
1088 |
|
|
}
|
1089 |
|
|
}
|
1090 |
|
|
|
1091 |
|
|
## ::tkcon::Prompt - displays the prompt in the console widget
|
1092 |
|
|
# ARGS: w - console text widget
|
1093 |
|
|
# Outputs: prompt (specified in ::tkcon::OPT(prompt1)) to console
|
1094 |
|
|
##
|
1095 |
|
|
proc ::tkcon::Prompt {{pre {}} {post {}} {prompt {}}} {
|
1096 |
|
|
variable OPT
|
1097 |
|
|
variable PRIV
|
1098 |
|
|
|
1099 |
|
|
set w $PRIV(console)
|
1100 |
|
|
if {[string compare {} $pre]} { $w insert end $pre stdout }
|
1101 |
|
|
set i [$w index end-1c]
|
1102 |
|
|
if {!$OPT(showstatusbar)} {
|
1103 |
|
|
if {[string compare {} $PRIV(appname)]} {
|
1104 |
|
|
$w insert end ">$PRIV(appname)< " prompt
|
1105 |
|
|
}
|
1106 |
|
|
if {[string compare :: $PRIV(namesp)]} {
|
1107 |
|
|
$w insert end "<$PRIV(namesp)> " prompt
|
1108 |
|
|
}
|
1109 |
|
|
}
|
1110 |
|
|
if {[string compare {} $prompt]} {
|
1111 |
|
|
$w insert end $prompt prompt
|
1112 |
|
|
} else {
|
1113 |
|
|
$w insert end [EvalSlave subst $OPT(prompt1)] prompt
|
1114 |
|
|
}
|
1115 |
|
|
$w mark set output $i
|
1116 |
|
|
$w mark set insert end
|
1117 |
|
|
$w mark set limit insert
|
1118 |
|
|
$w mark gravity limit left
|
1119 |
|
|
if {[string compare {} $post]} { $w insert end $post stdin }
|
1120 |
|
|
ConstrainBuffer $w $OPT(buffer)
|
1121 |
|
|
set ::tkcon::PRIV(StatusCursor) [$w index insert]
|
1122 |
|
|
$w see end
|
1123 |
|
|
}
|
1124 |
|
|
|
1125 |
|
|
## ::tkcon::About - gives about info for tkcon
|
1126 |
|
|
##
|
1127 |
|
|
proc ::tkcon::About {} {
|
1128 |
|
|
variable OPT
|
1129 |
|
|
variable PRIV
|
1130 |
|
|
variable COLOR
|
1131 |
|
|
|
1132 |
|
|
set w $PRIV(base).about
|
1133 |
|
|
if {[winfo exists $w]} {
|
1134 |
|
|
wm deiconify $w
|
1135 |
|
|
} else {
|
1136 |
|
|
global tk_patchLevel tcl_patchLevel tcl_version
|
1137 |
|
|
toplevel $w
|
1138 |
|
|
wm title $w "About tkcon v$PRIV(version)"
|
1139 |
|
|
button $w.b -text Dismiss -command [list wm withdraw $w]
|
1140 |
|
|
text $w.text -height 9 -bd 1 -width 60 \
|
1141 |
|
|
-foreground $COLOR(stdin) \
|
1142 |
|
|
-background $COLOR(bg) \
|
1143 |
|
|
-font $OPT(font)
|
1144 |
|
|
pack $w.b -fill x -side bottom
|
1145 |
|
|
pack $w.text -fill both -side left -expand 1
|
1146 |
|
|
$w.text tag config center -justify center
|
1147 |
|
|
$w.text tag config title -justify center -font {Courier -18 bold}
|
1148 |
|
|
# strip down the RCS info displayed in the about box
|
1149 |
|
|
regexp {,v ([0-9\./: ]*)} $PRIV(RCS) -> RCS
|
1150 |
|
|
$w.text insert 1.0 "About tkcon v$PRIV(version)" title \
|
1151 |
|
|
"\n\nCopyright 1995-2001 Jeffrey Hobbs, $PRIV(email)\
|
1152 |
|
|
\nRelease Info: v$PRIV(version), CVS v$RCS\
|
1153 |
|
|
\nDocumentation available at:\n$PRIV(docs)\
|
1154 |
|
|
\nUsing: Tcl v$tcl_patchLevel / Tk v$tk_patchLevel" center
|
1155 |
|
|
$w.text config -state disabled
|
1156 |
|
|
}
|
1157 |
|
|
}
|
1158 |
|
|
|
1159 |
|
|
## ::tkcon::InitMenus - inits the menubar and popup for the console
|
1160 |
|
|
# ARGS: w - console text widget
|
1161 |
|
|
##
|
1162 |
|
|
proc ::tkcon::InitMenus {w title} {
|
1163 |
|
|
variable OPT
|
1164 |
|
|
variable PRIV
|
1165 |
|
|
variable COLOR
|
1166 |
|
|
global tcl_platform
|
1167 |
|
|
|
1168 |
|
|
if {[catch {menu $w.pop -tearoff 0}]} {
|
1169 |
|
|
label $w.label -text "Menus not available in plugin mode"
|
1170 |
|
|
pack $w.label
|
1171 |
|
|
return
|
1172 |
|
|
}
|
1173 |
|
|
menu $w.context -tearoff 0 -disabledforeground $COLOR(disabled)
|
1174 |
|
|
set PRIV(context) $w.context
|
1175 |
|
|
set PRIV(popup) $w.pop
|
1176 |
|
|
|
1177 |
|
|
proc MenuButton {w m l} {
|
1178 |
|
|
$w add cascade -label $m -underline 0 -menu $w.$l
|
1179 |
|
|
return $w.$l
|
1180 |
|
|
}
|
1181 |
|
|
|
1182 |
|
|
foreach m [list File Console Edit Interp Prefs History Help] {
|
1183 |
|
|
set l [string tolower $m]
|
1184 |
|
|
MenuButton $w $m $l
|
1185 |
|
|
$w.pop add cascade -label $m -underline 0 -menu $w.pop.$l
|
1186 |
|
|
}
|
1187 |
|
|
|
1188 |
|
|
## File Menu
|
1189 |
|
|
##
|
1190 |
|
|
foreach m [list [menu $w.file -disabledforeground $COLOR(disabled)] \
|
1191 |
|
|
[menu $w.pop.file -disabledforeground $COLOR(disabled)]] {
|
1192 |
|
|
$m add command -label "Load File" -underline 0 -command ::tkcon::Load
|
1193 |
|
|
$m add cascade -label "Save ..." -underline 0 -menu $m.save
|
1194 |
|
|
$m add separator
|
1195 |
|
|
$m add command -label "Quit" -underline 0 -accel Ctrl-q -command exit
|
1196 |
|
|
|
1197 |
|
|
## Save Menu
|
1198 |
|
|
##
|
1199 |
|
|
set s $m.save
|
1200 |
|
|
menu $s -disabledforeground $COLOR(disabled) -tearoff 0
|
1201 |
|
|
$s add command -label "All" -underline 0 \
|
1202 |
|
|
-command {::tkcon::Save {} all}
|
1203 |
|
|
$s add command -label "History" -underline 0 \
|
1204 |
|
|
-command {::tkcon::Save {} history}
|
1205 |
|
|
$s add command -label "Stdin" -underline 3 \
|
1206 |
|
|
-command {::tkcon::Save {} stdin}
|
1207 |
|
|
$s add command -label "Stdout" -underline 3 \
|
1208 |
|
|
-command {::tkcon::Save {} stdout}
|
1209 |
|
|
$s add command -label "Stderr" -underline 3 \
|
1210 |
|
|
-command {::tkcon::Save {} stderr}
|
1211 |
|
|
}
|
1212 |
|
|
|
1213 |
|
|
## Console Menu
|
1214 |
|
|
##
|
1215 |
|
|
foreach m [list [menu $w.console -disabledfore $COLOR(disabled)] \
|
1216 |
|
|
[menu $w.pop.console -disabledfore $COLOR(disabled)]] {
|
1217 |
|
|
$m add command -label "$title Console" -state disabled
|
1218 |
|
|
$m add command -label "New Console" -underline 0 -accel Ctrl-N \
|
1219 |
|
|
-command ::tkcon::New
|
1220 |
|
|
$m add command -label "Close Console" -underline 0 -accel Ctrl-w \
|
1221 |
|
|
-command ::tkcon::Destroy
|
1222 |
|
|
$m add command -label "Clear Console" -underline 1 -accel Ctrl-l \
|
1223 |
|
|
-command { clear; ::tkcon::Prompt }
|
1224 |
|
|
if {[string match unix $tcl_platform(platform)]} {
|
1225 |
|
|
$m add separator
|
1226 |
|
|
$m add command -label "Make Xauth Secure" -und 5 \
|
1227 |
|
|
-command ::tkcon::XauthSecure
|
1228 |
|
|
}
|
1229 |
|
|
$m add separator
|
1230 |
|
|
$m add cascade -label "Attach To ..." -underline 0 -menu $m.attach
|
1231 |
|
|
|
1232 |
|
|
## Attach Console Menu
|
1233 |
|
|
##
|
1234 |
|
|
set sub [menu $m.attach -disabledforeground $COLOR(disabled)]
|
1235 |
|
|
$sub add cascade -label "Interpreter" -underline 0 -menu $sub.apps
|
1236 |
|
|
$sub add cascade -label "Namespace" -underline 1 -menu $sub.name
|
1237 |
|
|
$sub add cascade -label "Socket" -underline 1 -menu $sub.sock \
|
1238 |
|
|
-state [expr {([info tclversion] < 8.3)?"disabled":"normal"}]
|
1239 |
|
|
|
1240 |
|
|
## Attach Console Menu
|
1241 |
|
|
##
|
1242 |
|
|
menu $sub.apps -disabledforeground $COLOR(disabled) \
|
1243 |
|
|
-postcommand [list ::tkcon::AttachMenu $sub.apps]
|
1244 |
|
|
|
1245 |
|
|
## Attach Namespace Menu
|
1246 |
|
|
##
|
1247 |
|
|
menu $sub.name -disabledforeground $COLOR(disabled) -tearoff 0 \
|
1248 |
|
|
-postcommand [list ::tkcon::NamespaceMenu $sub.name]
|
1249 |
|
|
|
1250 |
|
|
if {$::tcl_version >= 8.3} {
|
1251 |
|
|
# This uses [file channels] to create the menu, so we only
|
1252 |
|
|
# want it for newer versions of Tcl.
|
1253 |
|
|
|
1254 |
|
|
## Attach Socket Menu
|
1255 |
|
|
##
|
1256 |
|
|
menu $sub.sock -disabledforeground $COLOR(disabled) -tearoff 0 \
|
1257 |
|
|
-postcommand [list ::tkcon::SocketMenu $sub.sock]
|
1258 |
|
|
}
|
1259 |
|
|
|
1260 |
|
|
## Attach Display Menu
|
1261 |
|
|
##
|
1262 |
|
|
if {![string compare "unix" $tcl_platform(platform)]} {
|
1263 |
|
|
$sub add cascade -label "Display" -und 1 -menu $sub.disp
|
1264 |
|
|
menu $sub.disp -disabledforeground $COLOR(disabled) \
|
1265 |
|
|
-tearoff 0 \
|
1266 |
|
|
-postcommand [list ::tkcon::DisplayMenu $sub.disp]
|
1267 |
|
|
}
|
1268 |
|
|
}
|
1269 |
|
|
|
1270 |
|
|
## Edit Menu
|
1271 |
|
|
##
|
1272 |
|
|
set text $PRIV(console)
|
1273 |
|
|
foreach m [list [menu $w.edit] [menu $w.pop.edit]] {
|
1274 |
|
|
$m add command -label "Cut" -underline 2 -accel Ctrl-x \
|
1275 |
|
|
-command [list ::tkcon::Cut $text]
|
1276 |
|
|
$m add command -label "Copy" -underline 0 -accel Ctrl-c \
|
1277 |
|
|
-command [list ::tkcon::Copy $text]
|
1278 |
|
|
$m add command -label "Paste" -underline 0 -accel Ctrl-v \
|
1279 |
|
|
-command [list ::tkcon::Paste $text]
|
1280 |
|
|
$m add separator
|
1281 |
|
|
$m add command -label "Find" -underline 0 -accel Ctrl-F \
|
1282 |
|
|
-command [list ::tkcon::FindBox $text]
|
1283 |
|
|
}
|
1284 |
|
|
|
1285 |
|
|
## Interp Menu
|
1286 |
|
|
##
|
1287 |
|
|
foreach m [list $w.interp $w.pop.interp] {
|
1288 |
|
|
menu $m -disabledforeground $COLOR(disabled) \
|
1289 |
|
|
-postcommand [list ::tkcon::InterpMenu $m]
|
1290 |
|
|
}
|
1291 |
|
|
|
1292 |
|
|
## Prefs Menu
|
1293 |
|
|
##
|
1294 |
|
|
foreach m [list [menu $w.prefs] [menu $w.pop.prefs]] {
|
1295 |
|
|
$m add check -label "Brace Highlighting" \
|
1296 |
|
|
-underline 0 -variable ::tkcon::OPT(lightbrace)
|
1297 |
|
|
$m add check -label "Command Highlighting" \
|
1298 |
|
|
-underline 0 -variable ::tkcon::OPT(lightcmd)
|
1299 |
|
|
$m add check -label "History Substitution" \
|
1300 |
|
|
-underline 0 -variable ::tkcon::OPT(subhistory)
|
1301 |
|
|
$m add check -label "Hot Errors" \
|
1302 |
|
|
-underline 0 -variable ::tkcon::OPT(hoterrors)
|
1303 |
|
|
$m add check -label "Non-Tcl Attachments" \
|
1304 |
|
|
-underline 0 -variable ::tkcon::OPT(nontcl)
|
1305 |
|
|
$m add check -label "Calculator Mode" \
|
1306 |
|
|
-underline 1 -variable ::tkcon::OPT(calcmode)
|
1307 |
|
|
$m add check -label "Show Multiple Matches" \
|
1308 |
|
|
-underline 0 -variable ::tkcon::OPT(showmultiple)
|
1309 |
|
|
$m add check -label "Show Menubar" \
|
1310 |
|
|
-underline 5 -variable ::tkcon::OPT(showmenu) \
|
1311 |
|
|
-command {$::tkcon::PRIV(root) configure -menu [expr \
|
1312 |
|
|
{$::tkcon::OPT(showmenu) ? $::tkcon::PRIV(menubar) : {}}]}
|
1313 |
|
|
$m add check -label "Show Statusbar" \
|
1314 |
|
|
-underline 5 -variable ::tkcon::OPT(showstatusbar) \
|
1315 |
|
|
-command {
|
1316 |
|
|
if {$::tkcon::OPT(showstatusbar)} {
|
1317 |
|
|
pack $::tkcon::PRIV(statusbar) -side bottom -fill x \
|
1318 |
|
|
-before $::tkcon::PRIV(scrolly)
|
1319 |
|
|
} else { pack forget $::tkcon::PRIV(statusbar) }
|
1320 |
|
|
}
|
1321 |
|
|
$m add cascade -label "Scrollbar" -underline 2 -menu $m.scroll
|
1322 |
|
|
|
1323 |
|
|
## Scrollbar Menu
|
1324 |
|
|
##
|
1325 |
|
|
set m [menu $m.scroll -tearoff 0]
|
1326 |
|
|
$m add radio -label "Left" -value left \
|
1327 |
|
|
-variable ::tkcon::OPT(scrollypos) \
|
1328 |
|
|
-command { pack config $::tkcon::PRIV(scrolly) -side left }
|
1329 |
|
|
$m add radio -label "Right" -value right \
|
1330 |
|
|
-variable ::tkcon::OPT(scrollypos) \
|
1331 |
|
|
-command { pack config $::tkcon::PRIV(scrolly) -side right }
|
1332 |
|
|
}
|
1333 |
|
|
|
1334 |
|
|
## History Menu
|
1335 |
|
|
##
|
1336 |
|
|
foreach m [list $w.history $w.pop.history] {
|
1337 |
|
|
menu $m -disabledforeground $COLOR(disabled) \
|
1338 |
|
|
-postcommand [list ::tkcon::HistoryMenu $m]
|
1339 |
|
|
}
|
1340 |
|
|
|
1341 |
|
|
## Help Menu
|
1342 |
|
|
##
|
1343 |
|
|
foreach m [list [menu $w.help] [menu $w.pop.help]] {
|
1344 |
|
|
$m add command -label "About " -underline 0 -accel Ctrl-A \
|
1345 |
|
|
-command ::tkcon::About
|
1346 |
|
|
$m add command -label "Retrieve Latest Version" -underline 0 \
|
1347 |
|
|
-command ::tkcon::Retrieve
|
1348 |
|
|
}
|
1349 |
|
|
}
|
1350 |
|
|
|
1351 |
|
|
## ::tkcon::HistoryMenu - dynamically build the menu for attached interpreters
|
1352 |
|
|
##
|
1353 |
|
|
# ARGS: m - menu widget
|
1354 |
|
|
##
|
1355 |
|
|
proc ::tkcon::HistoryMenu m {
|
1356 |
|
|
variable PRIV
|
1357 |
|
|
|
1358 |
|
|
if {![winfo exists $m]} return
|
1359 |
|
|
set id [EvalSlave history nextid]
|
1360 |
|
|
if {$PRIV(histid)==$id} return
|
1361 |
|
|
set PRIV(histid) $id
|
1362 |
|
|
$m delete 0 end
|
1363 |
|
|
while {($id>1) && ($id>$PRIV(histid)-10) && \
|
1364 |
|
|
![catch {EvalSlave history event [incr id -1]} tmp]} {
|
1365 |
|
|
set lbl $tmp
|
1366 |
|
|
if {[string len $lbl]>32} { set lbl [string range $tmp 0 28]... }
|
1367 |
|
|
$m add command -label "$id: $lbl" -command "
|
1368 |
|
|
$::tkcon::PRIV(console) delete limit end
|
1369 |
|
|
$::tkcon::PRIV(console) insert limit [list $tmp]
|
1370 |
|
|
$::tkcon::PRIV(console) see end
|
1371 |
|
|
::tkcon::Eval $::tkcon::PRIV(console)"
|
1372 |
|
|
}
|
1373 |
|
|
}
|
1374 |
|
|
|
1375 |
|
|
## ::tkcon::InterpMenu - dynamically build the menu for attached interpreters
|
1376 |
|
|
##
|
1377 |
|
|
# ARGS: w - menu widget
|
1378 |
|
|
##
|
1379 |
|
|
proc ::tkcon::InterpMenu w {
|
1380 |
|
|
variable OPT
|
1381 |
|
|
variable PRIV
|
1382 |
|
|
variable COLOR
|
1383 |
|
|
|
1384 |
|
|
if {![winfo exists $w]} return
|
1385 |
|
|
$w delete 0 end
|
1386 |
|
|
foreach {app type} [Attach] break
|
1387 |
|
|
$w add command -label "[string toupper $type]: $app" -state disabled
|
1388 |
|
|
if {($OPT(nontcl) && [string match interp $type]) || $PRIV(deadapp)} {
|
1389 |
|
|
$w add separator
|
1390 |
|
|
$w add command -state disabled -label "Communication disabled to"
|
1391 |
|
|
$w add command -state disabled -label "dead or non-Tcl interps"
|
1392 |
|
|
return
|
1393 |
|
|
}
|
1394 |
|
|
|
1395 |
|
|
## Show Last Error
|
1396 |
|
|
##
|
1397 |
|
|
$w add separator
|
1398 |
|
|
$w add command -label "Show Last Error" \
|
1399 |
|
|
-command [list tkcon error $app $type]
|
1400 |
|
|
|
1401 |
|
|
## Packages Cascaded Menu
|
1402 |
|
|
##
|
1403 |
|
|
$w add separator
|
1404 |
|
|
$w add cascade -label Packages -underline 0 -menu $w.pkg
|
1405 |
|
|
set m $w.pkg
|
1406 |
|
|
if {![winfo exists $m]} {
|
1407 |
|
|
menu $m -tearoff no -disabledforeground $COLOR(disabled) \
|
1408 |
|
|
-postcommand [list ::tkcon::PkgMenu $m $app $type]
|
1409 |
|
|
}
|
1410 |
|
|
|
1411 |
|
|
## State Checkpoint/Revert
|
1412 |
|
|
##
|
1413 |
|
|
$w add separator
|
1414 |
|
|
$w add command -label "Checkpoint State" \
|
1415 |
|
|
-command [list ::tkcon::StateCheckpoint $app $type]
|
1416 |
|
|
$w add command -label "Revert State" \
|
1417 |
|
|
-command [list ::tkcon::StateRevert $app $type]
|
1418 |
|
|
$w add command -label "View State Change" \
|
1419 |
|
|
-command [list ::tkcon::StateCompare $app $type]
|
1420 |
|
|
|
1421 |
|
|
## Init Interp
|
1422 |
|
|
##
|
1423 |
|
|
$w add separator
|
1424 |
|
|
$w add command -label "Send tkcon Commands" \
|
1425 |
|
|
-command [list ::tkcon::InitInterp $app $type]
|
1426 |
|
|
}
|
1427 |
|
|
|
1428 |
|
|
## ::tkcon::PkgMenu - fill in in the applications sub-menu
|
1429 |
|
|
## with a list of all the applications that currently exist.
|
1430 |
|
|
##
|
1431 |
|
|
proc ::tkcon::PkgMenu {m app type} {
|
1432 |
|
|
# just in case stuff has been added to the auto_path
|
1433 |
|
|
# we have to make sure that the errorInfo doesn't get screwed up
|
1434 |
|
|
EvalAttached {
|
1435 |
|
|
set __tkcon_error $errorInfo
|
1436 |
|
|
catch {package require bogus-package-name}
|
1437 |
|
|
set errorInfo ${__tkcon_error}
|
1438 |
|
|
unset __tkcon_error
|
1439 |
|
|
}
|
1440 |
|
|
$m delete 0 end
|
1441 |
|
|
foreach pkg [EvalAttached [list info loaded {}]] {
|
1442 |
|
|
set loaded([lindex $pkg 1]) [package provide $pkg]
|
1443 |
|
|
}
|
1444 |
|
|
foreach pkg [lremove [EvalAttached {package names}] Tcl] {
|
1445 |
|
|
set version [EvalAttached [list package provide $pkg]]
|
1446 |
|
|
if {[string compare {} $version]} {
|
1447 |
|
|
set loaded($pkg) $version
|
1448 |
|
|
} elseif {![info exists loaded($pkg)]} {
|
1449 |
|
|
set loadable($pkg) [list package require $pkg]
|
1450 |
|
|
}
|
1451 |
|
|
}
|
1452 |
|
|
foreach pkg [EvalAttached {info loaded}] {
|
1453 |
|
|
set pkg [lindex $pkg 1]
|
1454 |
|
|
if {![info exists loaded($pkg)] && ![info exists loadable($pkg)]} {
|
1455 |
|
|
set loadable($pkg) [list load {} $pkg]
|
1456 |
|
|
}
|
1457 |
|
|
}
|
1458 |
|
|
set npkg 0
|
1459 |
|
|
foreach pkg [lsort -dictionary [array names loadable]] {
|
1460 |
|
|
foreach v [EvalAttached [list package version $pkg]] {
|
1461 |
|
|
set brkcol [expr {([incr npkg]%16)==0}]
|
1462 |
|
|
$m add command -label "Load $pkg ($v)" -command \
|
1463 |
|
|
"::tkcon::EvalOther [list $app] $type $loadable($pkg) $v" \
|
1464 |
|
|
-columnbreak $brkcol
|
1465 |
|
|
}
|
1466 |
|
|
}
|
1467 |
|
|
if {[info exists loaded] && [info exists loadable]} {
|
1468 |
|
|
$m add separator
|
1469 |
|
|
}
|
1470 |
|
|
foreach pkg [lsort -dictionary [array names loaded]] {
|
1471 |
|
|
$m add command -label "${pkg}$loaded($pkg) Loaded" -state disabled
|
1472 |
|
|
}
|
1473 |
|
|
}
|
1474 |
|
|
|
1475 |
|
|
## ::tkcon::AttachMenu - fill in in the applications sub-menu
|
1476 |
|
|
## with a list of all the applications that currently exist.
|
1477 |
|
|
##
|
1478 |
|
|
proc ::tkcon::AttachMenu m {
|
1479 |
|
|
variable OPT
|
1480 |
|
|
variable PRIV
|
1481 |
|
|
|
1482 |
|
|
array set interps [set tmp [Interps]]
|
1483 |
|
|
foreach {i j} $tmp { set tknames($j) {} }
|
1484 |
|
|
|
1485 |
|
|
$m delete 0 end
|
1486 |
|
|
set cmd {::tkcon::Prompt \n [::tkcon::CmdGet $::tkcon::PRIV(console)]}
|
1487 |
|
|
$m add radio -label {None (use local slave) } -accel Ctrl-1 \
|
1488 |
|
|
-variable ::tkcon::PRIV(app) \
|
1489 |
|
|
-value [concat $::tkcon::PRIV(name) $::tkcon::OPT(exec)] \
|
1490 |
|
|
-command "::tkcon::Attach {}; $cmd"
|
1491 |
|
|
$m add separator
|
1492 |
|
|
$m add command -label "Foreign Tk Interpreters" -state disabled
|
1493 |
|
|
foreach i [lsort [lremove [winfo interps] [array names tknames]]] {
|
1494 |
|
|
$m add radio -label $i -variable ::tkcon::PRIV(app) -value $i \
|
1495 |
|
|
-command "::tkcon::Attach [list $i] interp; $cmd"
|
1496 |
|
|
}
|
1497 |
|
|
$m add separator
|
1498 |
|
|
|
1499 |
|
|
$m add command -label "tkcon Interpreters" -state disabled
|
1500 |
|
|
foreach i [lsort [array names interps]] {
|
1501 |
|
|
if {[string match {} $interps($i)]} { set interps($i) "no Tk" }
|
1502 |
|
|
if {[regexp {^Slave[0-9]+} $i]} {
|
1503 |
|
|
set opts [list -label "$i ($interps($i))" \
|
1504 |
|
|
-variable ::tkcon::PRIV(app) -value $i \
|
1505 |
|
|
-command "::tkcon::Attach [list $i] slave; $cmd"]
|
1506 |
|
|
if {[string match $PRIV(name) $i]} {
|
1507 |
|
|
append opts " -accel Ctrl-2"
|
1508 |
|
|
}
|
1509 |
|
|
eval $m add radio $opts
|
1510 |
|
|
} else {
|
1511 |
|
|
set name [concat Main $i]
|
1512 |
|
|
if {[string match Main $name]} {
|
1513 |
|
|
$m add radio -label "$name ($interps($i))" -accel Ctrl-3 \
|
1514 |
|
|
-variable ::tkcon::PRIV(app) -value Main \
|
1515 |
|
|
-command "::tkcon::Attach [list $name] slave; $cmd"
|
1516 |
|
|
} else {
|
1517 |
|
|
$m add radio -label "$name ($interps($i))" \
|
1518 |
|
|
-variable ::tkcon::PRIV(app) -value $i \
|
1519 |
|
|
-command "::tkcon::Attach [list $name] slave; $cmd"
|
1520 |
|
|
}
|
1521 |
|
|
}
|
1522 |
|
|
}
|
1523 |
|
|
}
|
1524 |
|
|
|
1525 |
|
|
## Displays Cascaded Menu
|
1526 |
|
|
##
|
1527 |
|
|
proc ::tkcon::DisplayMenu m {
|
1528 |
|
|
$m delete 0 end
|
1529 |
|
|
set cmd {::tkcon::Prompt \n [::tkcon::CmdGet $::tkcon::PRIV(console)]}
|
1530 |
|
|
|
1531 |
|
|
$m add command -label "New Display" -command ::tkcon::NewDisplay
|
1532 |
|
|
foreach disp [Display] {
|
1533 |
|
|
$m add separator
|
1534 |
|
|
$m add command -label $disp -state disabled
|
1535 |
|
|
set res [Display $disp]
|
1536 |
|
|
set win [lindex $res 0]
|
1537 |
|
|
foreach i [lsort [lindex $res 1]] {
|
1538 |
|
|
$m add radio -label $i -variable ::tkcon::PRIV(app) -value $i \
|
1539 |
|
|
-command "::tkcon::Attach [list $i] [list dpy:$win]; $cmd"
|
1540 |
|
|
}
|
1541 |
|
|
}
|
1542 |
|
|
}
|
1543 |
|
|
|
1544 |
|
|
## Sockets Cascaded Menu
|
1545 |
|
|
##
|
1546 |
|
|
proc ::tkcon::SocketMenu m {
|
1547 |
|
|
$m delete 0 end
|
1548 |
|
|
set cmd {::tkcon::Prompt \n [::tkcon::CmdGet $::tkcon::PRIV(console)]}
|
1549 |
|
|
|
1550 |
|
|
$m add command -label "Create Connection" \
|
1551 |
|
|
-command "::tkcon::NewSocket; $cmd"
|
1552 |
|
|
foreach sock [file channels sock*] {
|
1553 |
|
|
$m add radio -label $sock -variable ::tkcon::PRIV(app) -value $sock \
|
1554 |
|
|
-command "::tkcon::Attach $sock socket; $cmd"
|
1555 |
|
|
}
|
1556 |
|
|
}
|
1557 |
|
|
|
1558 |
|
|
## Namepaces Cascaded Menu
|
1559 |
|
|
##
|
1560 |
|
|
proc ::tkcon::NamespaceMenu m {
|
1561 |
|
|
variable PRIV
|
1562 |
|
|
variable OPT
|
1563 |
|
|
|
1564 |
|
|
$m delete 0 end
|
1565 |
|
|
if {($PRIV(deadapp) || [string match socket $PRIV(apptype)] || \
|
1566 |
|
|
($OPT(nontcl) && [string match interp $PRIV(apptype)]))} {
|
1567 |
|
|
$m add command -label "No Namespaces" -state disabled
|
1568 |
|
|
return
|
1569 |
|
|
}
|
1570 |
|
|
|
1571 |
|
|
## Same command as for ::tkcon::AttachMenu items
|
1572 |
|
|
set cmd {::tkcon::Prompt \n [::tkcon::CmdGet $::tkcon::PRIV(console)]}
|
1573 |
|
|
|
1574 |
|
|
set names [lsort [Namespaces ::]]
|
1575 |
|
|
if {[llength $names] > $OPT(maxmenu)} {
|
1576 |
|
|
$m add command -label "Attached to $PRIV(namesp)" -state disabled
|
1577 |
|
|
$m add command -label "List Namespaces" \
|
1578 |
|
|
-command [list ::tkcon::NamespacesList $names]
|
1579 |
|
|
} else {
|
1580 |
|
|
foreach i $names {
|
1581 |
|
|
if {[string match :: $i]} {
|
1582 |
|
|
$m add radio -label "Main" -value $i \
|
1583 |
|
|
-variable ::tkcon::PRIV(namesp) \
|
1584 |
|
|
-command "::tkcon::AttachNamespace [list $i]; $cmd"
|
1585 |
|
|
} else {
|
1586 |
|
|
$m add radio -label $i -value $i \
|
1587 |
|
|
-variable ::tkcon::PRIV(namesp) \
|
1588 |
|
|
-command "::tkcon::AttachNamespace [list $i]; $cmd"
|
1589 |
|
|
}
|
1590 |
|
|
}
|
1591 |
|
|
}
|
1592 |
|
|
}
|
1593 |
|
|
|
1594 |
|
|
## Namepaces List
|
1595 |
|
|
##
|
1596 |
|
|
proc ::tkcon::NamespacesList {names} {
|
1597 |
|
|
variable PRIV
|
1598 |
|
|
|
1599 |
|
|
set f $PRIV(base).namespaces
|
1600 |
|
|
catch {destroy $f}
|
1601 |
|
|
toplevel $f
|
1602 |
|
|
listbox $f.names -width 30 -height 15 -selectmode single \
|
1603 |
|
|
-yscrollcommand [list $f.scrollv set] \
|
1604 |
|
|
-xscrollcommand [list $f.scrollh set]
|
1605 |
|
|
scrollbar $f.scrollv -command [list $f.names yview]
|
1606 |
|
|
scrollbar $f.scrollh -command [list $f.names xview] -orient horizontal
|
1607 |
|
|
frame $f.buttons
|
1608 |
|
|
button $f.cancel -text "Cancel" -command [list destroy $f]
|
1609 |
|
|
|
1610 |
|
|
grid $f.names $f.scrollv -sticky nesw
|
1611 |
|
|
grid $f.scrollh -sticky ew
|
1612 |
|
|
grid $f.buttons -sticky nesw
|
1613 |
|
|
grid $f.cancel -in $f.buttons -pady 6
|
1614 |
|
|
|
1615 |
|
|
grid columnconfigure $f 0 -weight 1
|
1616 |
|
|
grid rowconfigure $f 0 -weight 1
|
1617 |
|
|
#fill the listbox
|
1618 |
|
|
foreach i $names {
|
1619 |
|
|
if {[string match :: $i]} {
|
1620 |
|
|
$f.names insert 0 Main
|
1621 |
|
|
} else {
|
1622 |
|
|
$f.names insert end $i
|
1623 |
|
|
}
|
1624 |
|
|
}
|
1625 |
|
|
#Bindings
|
1626 |
|
|
bind $f.names <Double-1> {
|
1627 |
|
|
## Catch in case the namespace disappeared on us
|
1628 |
|
|
catch { ::tkcon::AttachNamespace [%W get [%W nearest %y]] }
|
1629 |
|
|
::tkcon::Prompt "\n" [::tkcon::CmdGet $::tkcon::PRIV(console)]
|
1630 |
|
|
destroy [winfo toplevel %W]
|
1631 |
|
|
}
|
1632 |
|
|
}
|
1633 |
|
|
|
1634 |
|
|
# ::tkcon::XauthSecure --
|
1635 |
|
|
#
|
1636 |
|
|
# This removes all the names in the xhost list, and secures
|
1637 |
|
|
# the display for Tk send commands. Of course, this prevents
|
1638 |
|
|
# what might have been otherwise allowable X connections
|
1639 |
|
|
#
|
1640 |
|
|
# Arguments:
|
1641 |
|
|
# none
|
1642 |
|
|
# Results:
|
1643 |
|
|
# Returns nothing
|
1644 |
|
|
#
|
1645 |
|
|
proc ::tkcon::XauthSecure {} {
|
1646 |
|
|
global tcl_platform
|
1647 |
|
|
|
1648 |
|
|
if {[string compare unix $tcl_platform(platform)]} {
|
1649 |
|
|
# This makes no sense outside of Unix
|
1650 |
|
|
return
|
1651 |
|
|
}
|
1652 |
|
|
set hosts [exec xhost]
|
1653 |
|
|
# the first line is info only
|
1654 |
|
|
foreach host [lrange [split $hosts \n] 1 end] {
|
1655 |
|
|
exec xhost -$host
|
1656 |
|
|
}
|
1657 |
|
|
exec xhost -
|
1658 |
|
|
tk_messageBox -title "Xhost secured" -message "Xhost secured" -icon info
|
1659 |
|
|
}
|
1660 |
|
|
|
1661 |
|
|
## ::tkcon::FindBox - creates minimal dialog interface to ::tkcon::Find
|
1662 |
|
|
# ARGS: w - text widget
|
1663 |
|
|
# str - optional seed string for ::tkcon::PRIV(find)
|
1664 |
|
|
##
|
1665 |
|
|
proc ::tkcon::FindBox {w {str {}}} {
|
1666 |
|
|
variable PRIV
|
1667 |
|
|
|
1668 |
|
|
set base $PRIV(base).find
|
1669 |
|
|
if {![winfo exists $base]} {
|
1670 |
|
|
toplevel $base
|
1671 |
|
|
wm withdraw $base
|
1672 |
|
|
wm title $base "tkcon Find"
|
1673 |
|
|
|
1674 |
|
|
pack [frame $base.f] -fill x -expand 1
|
1675 |
|
|
label $base.f.l -text "Find:"
|
1676 |
|
|
entry $base.f.e -textvariable ::tkcon::PRIV(find)
|
1677 |
|
|
pack [frame $base.opt] -fill x
|
1678 |
|
|
checkbutton $base.opt.c -text "Case Sensitive" \
|
1679 |
|
|
-variable ::tkcon::PRIV(find,case)
|
1680 |
|
|
checkbutton $base.opt.r -text "Use Regexp" -variable ::tkcon::PRIV(find,reg)
|
1681 |
|
|
pack $base.f.l -side left
|
1682 |
|
|
pack $base.f.e $base.opt.c $base.opt.r -side left -fill both -expand 1
|
1683 |
|
|
pack [frame $base.sep -bd 2 -relief sunken -height 4] -fill x
|
1684 |
|
|
pack [frame $base.btn] -fill both
|
1685 |
|
|
button $base.btn.fnd -text "Find" -width 6
|
1686 |
|
|
button $base.btn.clr -text "Clear" -width 6
|
1687 |
|
|
button $base.btn.dis -text "Dismiss" -width 6
|
1688 |
|
|
eval pack [winfo children $base.btn] -padx 4 -pady 2 \
|
1689 |
|
|
-side left -fill both
|
1690 |
|
|
|
1691 |
|
|
focus $base.f.e
|
1692 |
|
|
|
1693 |
|
|
bind $base.f.e <Return> [list $base.btn.fnd invoke]
|
1694 |
|
|
bind $base.f.e <Escape> [list $base.btn.dis invoke]
|
1695 |
|
|
}
|
1696 |
|
|
$base.btn.fnd config -command "::tkcon::Find [list $w] \$::tkcon::PRIV(find) \
|
1697 |
|
|
-case \$::tkcon::PRIV(find,case) -reg \$::tkcon::PRIV(find,reg)"
|
1698 |
|
|
$base.btn.clr config -command "
|
1699 |
|
|
[list $w] tag remove find 1.0 end
|
1700 |
|
|
set ::tkcon::PRIV(find) {}
|
1701 |
|
|
"
|
1702 |
|
|
$base.btn.dis config -command "
|
1703 |
|
|
[list $w] tag remove find 1.0 end
|
1704 |
|
|
wm withdraw [list $base]
|
1705 |
|
|
"
|
1706 |
|
|
if {[string compare {} $str]} {
|
1707 |
|
|
set PRIV(find) $str
|
1708 |
|
|
$base.btn.fnd invoke
|
1709 |
|
|
}
|
1710 |
|
|
|
1711 |
|
|
if {[string compare normal [wm state $base]]} {
|
1712 |
|
|
wm deiconify $base
|
1713 |
|
|
} else { raise $base }
|
1714 |
|
|
$base.f.e select range 0 end
|
1715 |
|
|
}
|
1716 |
|
|
|
1717 |
|
|
## ::tkcon::Find - searches in text widget $w for $str and highlights it
|
1718 |
|
|
## If $str is empty, it just deletes any highlighting
|
1719 |
|
|
# ARGS: w - text widget
|
1720 |
|
|
# str - string to search for
|
1721 |
|
|
# -case TCL_BOOLEAN whether to be case sensitive DEFAULT: 0
|
1722 |
|
|
# -regexp TCL_BOOLEAN whether to use $str as pattern DEFAULT: 0
|
1723 |
|
|
##
|
1724 |
|
|
proc ::tkcon::Find {w str args} {
|
1725 |
|
|
$w tag remove find 1.0 end
|
1726 |
|
|
set truth {^(1|yes|true|on)$}
|
1727 |
|
|
set opts {}
|
1728 |
|
|
foreach {key val} $args {
|
1729 |
|
|
switch -glob -- $key {
|
1730 |
|
|
-c* { if {[regexp -nocase $truth $val]} { set case 1 } }
|
1731 |
|
|
-r* { if {[regexp -nocase $truth $val]} { lappend opts -regexp } }
|
1732 |
|
|
default { return -code error "Unknown option $key" }
|
1733 |
|
|
}
|
1734 |
|
|
}
|
1735 |
|
|
if {![info exists case]} { lappend opts -nocase }
|
1736 |
|
|
if {[string match {} $str]} return
|
1737 |
|
|
$w mark set findmark 1.0
|
1738 |
|
|
while {[string compare {} [set ix [eval $w search $opts -count numc -- \
|
1739 |
|
|
[list $str] findmark end]]]} {
|
1740 |
|
|
$w tag add find $ix ${ix}+${numc}c
|
1741 |
|
|
$w mark set findmark ${ix}+1c
|
1742 |
|
|
}
|
1743 |
|
|
$w tag configure find -background $::tkcon::COLOR(blink)
|
1744 |
|
|
catch {$w see find.first}
|
1745 |
|
|
return [expr {[llength [$w tag ranges find]]/2}]
|
1746 |
|
|
}
|
1747 |
|
|
|
1748 |
|
|
## ::tkcon::Attach - called to attach tkcon to an interpreter
|
1749 |
|
|
# ARGS: name - application name to which tkcon sends commands
|
1750 |
|
|
# This is either a slave interperter name or tk appname.
|
1751 |
|
|
# type - (slave|interp) type of interpreter we're attaching to
|
1752 |
|
|
# slave means it's a tkcon interpreter
|
1753 |
|
|
# interp means we'll need to 'send' to it.
|
1754 |
|
|
# Results: ::tkcon::EvalAttached is recreated to evaluate in the
|
1755 |
|
|
# appropriate interpreter
|
1756 |
|
|
##
|
1757 |
|
|
proc ::tkcon::Attach {{name <NONE>} {type slave}} {
|
1758 |
|
|
variable PRIV
|
1759 |
|
|
variable OPT
|
1760 |
|
|
|
1761 |
|
|
if {[llength [info level 0]] == 1} {
|
1762 |
|
|
# no args were specified, return the attach info instead
|
1763 |
|
|
if {[string match {} $PRIV(appname)]} {
|
1764 |
|
|
return [list [concat $PRIV(name) $OPT(exec)] $PRIV(apptype)]
|
1765 |
|
|
} else {
|
1766 |
|
|
return [list $PRIV(appname) $PRIV(apptype)]
|
1767 |
|
|
}
|
1768 |
|
|
}
|
1769 |
|
|
set path [concat $PRIV(name) $OPT(exec)]
|
1770 |
|
|
|
1771 |
|
|
set PRIV(displayWin) .
|
1772 |
|
|
if {[string match namespace $type]} {
|
1773 |
|
|
return [uplevel 1 ::tkcon::AttachNamespace $name]
|
1774 |
|
|
} elseif {[string match dpy:* $type]} {
|
1775 |
|
|
set PRIV(displayWin) [string range $type 4 end]
|
1776 |
|
|
} elseif {[string match sock* $type]} {
|
1777 |
|
|
global tcl_version
|
1778 |
|
|
if {[catch {eof $name} res]} {
|
1779 |
|
|
return -code error "No known channel \"$name\""
|
1780 |
|
|
} elseif {$res} {
|
1781 |
|
|
catch {close $name}
|
1782 |
|
|
return -code error "Channel \"$name\" returned EOF"
|
1783 |
|
|
}
|
1784 |
|
|
set app $name
|
1785 |
|
|
set type socket
|
1786 |
|
|
} elseif {[string compare {} $name]} {
|
1787 |
|
|
array set interps [Interps]
|
1788 |
|
|
if {[string match {[Mm]ain} [lindex $name 0]]} {
|
1789 |
|
|
set name [lrange $name 1 end]
|
1790 |
|
|
}
|
1791 |
|
|
if {[string match $path $name]} {
|
1792 |
|
|
set name {}
|
1793 |
|
|
set app $path
|
1794 |
|
|
set type slave
|
1795 |
|
|
} elseif {[info exists interps($name)]} {
|
1796 |
|
|
if {[string match {} $name]} { set name Main; set app Main }
|
1797 |
|
|
set type slave
|
1798 |
|
|
} elseif {[interp exists $name]} {
|
1799 |
|
|
set name [concat $PRIV(name) $name]
|
1800 |
|
|
set type slave
|
1801 |
|
|
} elseif {[interp exists [concat $OPT(exec) $name]]} {
|
1802 |
|
|
set name [concat $path $name]
|
1803 |
|
|
set type slave
|
1804 |
|
|
} elseif {[lsearch -exact [winfo interps] $name] > -1} {
|
1805 |
|
|
if {[EvalSlave info exists tk_library] \
|
1806 |
|
|
&& [string match $name [EvalSlave tk appname]]} {
|
1807 |
|
|
set name {}
|
1808 |
|
|
set app $path
|
1809 |
|
|
set type slave
|
1810 |
|
|
} elseif {[set i [lsearch -exact \
|
1811 |
|
|
[Main set ::tkcon::PRIV(interps)] $name]] != -1} {
|
1812 |
|
|
set name [lindex [Main set ::tkcon::PRIV(slaves)] $i]
|
1813 |
|
|
if {[string match {[Mm]ain} $name]} { set app Main }
|
1814 |
|
|
set type slave
|
1815 |
|
|
} else {
|
1816 |
|
|
set type interp
|
1817 |
|
|
}
|
1818 |
|
|
} else {
|
1819 |
|
|
return -code error "No known interpreter \"$name\""
|
1820 |
|
|
}
|
1821 |
|
|
} else {
|
1822 |
|
|
set app $path
|
1823 |
|
|
}
|
1824 |
|
|
if {![info exists app]} { set app $name }
|
1825 |
|
|
array set PRIV [list app $app appname $name apptype $type deadapp 0]
|
1826 |
|
|
|
1827 |
|
|
## ::tkcon::EvalAttached - evaluates the args in the attached interp
|
1828 |
|
|
## args should be passed to this procedure as if they were being
|
1829 |
|
|
## passed to the 'eval' procedure. This procedure is dynamic to
|
1830 |
|
|
## ensure evaluation occurs in the right interp.
|
1831 |
|
|
# ARGS: args - the command and args to evaluate
|
1832 |
|
|
##
|
1833 |
|
|
switch -glob -- $type {
|
1834 |
|
|
slave {
|
1835 |
|
|
if {[string match {} $name]} {
|
1836 |
|
|
interp alias {} ::tkcon::EvalAttached {} \
|
1837 |
|
|
::tkcon::EvalSlave uplevel \#0
|
1838 |
|
|
} elseif {[string match Main $PRIV(app)]} {
|
1839 |
|
|
interp alias {} ::tkcon::EvalAttached {} ::tkcon::Main
|
1840 |
|
|
} elseif {[string match $PRIV(name) $PRIV(app)]} {
|
1841 |
|
|
interp alias {} ::tkcon::EvalAttached {} uplevel \#0
|
1842 |
|
|
} else {
|
1843 |
|
|
interp alias {} ::tkcon::EvalAttached {} \
|
1844 |
|
|
::tkcon::Slave $::tkcon::PRIV(app)
|
1845 |
|
|
}
|
1846 |
|
|
}
|
1847 |
|
|
sock* {
|
1848 |
|
|
interp alias {} ::tkcon::EvalAttached {} \
|
1849 |
|
|
::tkcon::EvalSlave uplevel \#0
|
1850 |
|
|
# The file event will just puts whatever data is found
|
1851 |
|
|
# into the interpreter
|
1852 |
|
|
fconfigure $name -buffering line -blocking 0
|
1853 |
|
|
fileevent $name readable ::tkcon::EvalSocketEvent
|
1854 |
|
|
}
|
1855 |
|
|
dpy:* -
|
1856 |
|
|
interp {
|
1857 |
|
|
if {$OPT(nontcl)} {
|
1858 |
|
|
interp alias {} ::tkcon::EvalAttached {} ::tkcon::EvalSlave
|
1859 |
|
|
set PRIV(namesp) ::
|
1860 |
|
|
} else {
|
1861 |
|
|
interp alias {} ::tkcon::EvalAttached {} ::tkcon::EvalSend
|
1862 |
|
|
}
|
1863 |
|
|
}
|
1864 |
|
|
default {
|
1865 |
|
|
return -code error "[lindex [info level 0] 0] did not specify\
|
1866 |
|
|
a valid type: must be slave or interp"
|
1867 |
|
|
}
|
1868 |
|
|
}
|
1869 |
|
|
if {[string match slave $type] || \
|
1870 |
|
|
(!$OPT(nontcl) && [regexp {^(interp|dpy)} $type])} {
|
1871 |
|
|
set PRIV(namesp) ::
|
1872 |
|
|
}
|
1873 |
|
|
set PRIV(StatusAttach) "$PRIV(app) ($PRIV(apptype))"
|
1874 |
|
|
return
|
1875 |
|
|
}
|
1876 |
|
|
|
1877 |
|
|
## ::tkcon::AttachNamespace - called to attach tkcon to a namespace
|
1878 |
|
|
# ARGS: name - namespace name in which tkcon should eval commands
|
1879 |
|
|
# Results: ::tkcon::EvalAttached will be modified
|
1880 |
|
|
##
|
1881 |
|
|
proc ::tkcon::AttachNamespace { name } {
|
1882 |
|
|
variable PRIV
|
1883 |
|
|
variable OPT
|
1884 |
|
|
|
1885 |
|
|
if {($OPT(nontcl) && [string match interp $PRIV(apptype)]) \
|
1886 |
|
|
|| [string match socket $PRIV(apptype)] \
|
1887 |
|
|
|| $PRIV(deadapp)} {
|
1888 |
|
|
return -code error "can't attach to namespace in attached environment"
|
1889 |
|
|
}
|
1890 |
|
|
if {[string match Main $name]} {set name ::}
|
1891 |
|
|
if {[string compare {} $name] && \
|
1892 |
|
|
[lsearch [Namespaces ::] $name] == -1} {
|
1893 |
|
|
return -code error "No known namespace \"$name\""
|
1894 |
|
|
}
|
1895 |
|
|
if {[regexp {^(|::)$} $name]} {
|
1896 |
|
|
## If name=={} || ::, we want the primary namespace
|
1897 |
|
|
set alias [interp alias {} ::tkcon::EvalAttached]
|
1898 |
|
|
if {[string match ::tkcon::EvalNamespace* $alias]} {
|
1899 |
|
|
eval [list interp alias {} ::tkcon::EvalAttached {}] \
|
1900 |
|
|
[lindex $alias 1]
|
1901 |
|
|
}
|
1902 |
|
|
set name ::
|
1903 |
|
|
} else {
|
1904 |
|
|
interp alias {} ::tkcon::EvalAttached {} ::tkcon::EvalNamespace \
|
1905 |
|
|
[interp alias {} ::tkcon::EvalAttached] [list $name]
|
1906 |
|
|
}
|
1907 |
|
|
set PRIV(namesp) $name
|
1908 |
|
|
set PRIV(StatusAttach) "$PRIV(app) $PRIV(namesp) ($PRIV(apptype))"
|
1909 |
|
|
}
|
1910 |
|
|
|
1911 |
|
|
## ::tkcon::NewSocket - called to create a socket to connect to
|
1912 |
|
|
# ARGS: none
|
1913 |
|
|
# Results: It will create a socket, and attach if requested
|
1914 |
|
|
##
|
1915 |
|
|
proc ::tkcon::NewSocket {} {
|
1916 |
|
|
variable PRIV
|
1917 |
|
|
|
1918 |
|
|
set t $PRIV(base).newsock
|
1919 |
|
|
if {![winfo exists $t]} {
|
1920 |
|
|
toplevel $t
|
1921 |
|
|
wm withdraw $t
|
1922 |
|
|
wm title $t "tkcon Create Socket"
|
1923 |
|
|
label $t.lhost -text "Host: "
|
1924 |
|
|
entry $t.host -width 20
|
1925 |
|
|
label $t.lport -text "Port: "
|
1926 |
|
|
entry $t.port -width 4
|
1927 |
|
|
button $t.ok -text "OK" -command {set ::tkcon::PRIV(grab) 1}
|
1928 |
|
|
bind $t.host <Return> [list focus $t.port]
|
1929 |
|
|
bind $t.port <Return> [list focus $t.ok]
|
1930 |
|
|
bind $t.ok <Return> [list $t.ok invoke]
|
1931 |
|
|
grid $t.lhost $t.host $t.lport $t.port -sticky ew
|
1932 |
|
|
grid $t.ok - - - -sticky ew
|
1933 |
|
|
grid columnconfig $t 1 -weight 1
|
1934 |
|
|
grid rowconfigure $t 1 -weight 1
|
1935 |
|
|
wm transient $t $PRIV(root)
|
1936 |
|
|
wm geometry $t +[expr {([winfo screenwidth $t]-[winfo \
|
1937 |
|
|
reqwidth $t]) / 2}]+[expr {([winfo \
|
1938 |
|
|
screenheight $t]-[winfo reqheight $t]) / 2}]
|
1939 |
|
|
}
|
1940 |
|
|
#$t.host delete 0 end
|
1941 |
|
|
#$t.port delete 0 end
|
1942 |
|
|
wm deiconify $t
|
1943 |
|
|
raise $t
|
1944 |
|
|
grab $t
|
1945 |
|
|
focus $t.host
|
1946 |
|
|
vwait ::tkcon::PRIV(grab)
|
1947 |
|
|
grab release $t
|
1948 |
|
|
wm withdraw $t
|
1949 |
|
|
set host [$t.host get]
|
1950 |
|
|
set port [$t.port get]
|
1951 |
|
|
if {$host == ""} { return }
|
1952 |
|
|
if {[catch {
|
1953 |
|
|
set sock [socket $host $port]
|
1954 |
|
|
} err]} {
|
1955 |
|
|
tk_messageBox -title "Socket Connection Error" \
|
1956 |
|
|
-message "Unable to connect to \"$host:$port\":\n$err" \
|
1957 |
|
|
-icon error -type ok
|
1958 |
|
|
} else {
|
1959 |
|
|
Attach $sock socket
|
1960 |
|
|
}
|
1961 |
|
|
}
|
1962 |
|
|
|
1963 |
|
|
## ::tkcon::Load - sources a file into the console
|
1964 |
|
|
## The file is actually sourced in the currently attached's interp
|
1965 |
|
|
# ARGS: fn - (optional) filename to source in
|
1966 |
|
|
# Returns: selected filename ({} if nothing was selected)
|
1967 |
|
|
##
|
1968 |
|
|
proc ::tkcon::Load { {fn ""} } {
|
1969 |
|
|
set types {
|
1970 |
|
|
{{Tcl Files} {.tcl .tk}}
|
1971 |
|
|
{{Text Files} {.txt}}
|
1972 |
|
|
{{All Files} *}
|
1973 |
|
|
}
|
1974 |
|
|
if {
|
1975 |
|
|
[string match {} $fn] &&
|
1976 |
|
|
([catch {tk_getOpenFile -filetypes $types \
|
1977 |
|
|
-title "Source File"} fn] || [string match {} $fn])
|
1978 |
|
|
} { return }
|
1979 |
|
|
EvalAttached [list source $fn]
|
1980 |
|
|
}
|
1981 |
|
|
|
1982 |
|
|
## ::tkcon::Save - saves the console or other widget buffer to a file
|
1983 |
|
|
## This does not eval in a slave because it's not necessary
|
1984 |
|
|
# ARGS: w - console text widget
|
1985 |
|
|
# fn - (optional) filename to save to
|
1986 |
|
|
##
|
1987 |
|
|
proc ::tkcon::Save { {fn ""} {type ""} {opt ""} {mode w} } {
|
1988 |
|
|
variable PRIV
|
1989 |
|
|
|
1990 |
|
|
if {![regexp -nocase {^(all|history|stdin|stdout|stderr|widget)$} $type]} {
|
1991 |
|
|
array set s { 0 All 1 History 2 Stdin 3 Stdout 4 Stderr 5 Cancel }
|
1992 |
|
|
## Allow user to specify what kind of stuff to save
|
1993 |
|
|
set type [tk_dialog $PRIV(base).savetype "Save Type" \
|
1994 |
|
|
"What part of the text do you want to save?" \
|
1995 |
|
|
questhead 0 $s(0) $s(1) $s(2) $s(3) $s(4) $s(5)]
|
1996 |
|
|
if {$type == 5 || $type == -1} return
|
1997 |
|
|
set type $s($type)
|
1998 |
|
|
}
|
1999 |
|
|
if {[string match {} $fn]} {
|
2000 |
|
|
set types {
|
2001 |
|
|
{{Tcl Files} {.tcl .tk}}
|
2002 |
|
|
{{Text Files} {.txt}}
|
2003 |
|
|
{{All Files} *}
|
2004 |
|
|
}
|
2005 |
|
|
if {[catch {tk_getSaveFile -defaultextension .tcl -filetypes $types \
|
2006 |
|
|
-title "Save $type"} fn] || [string match {} $fn]} return
|
2007 |
|
|
}
|
2008 |
|
|
set type [string tolower $type]
|
2009 |
|
|
switch $type {
|
2010 |
|
|
stdin - stdout - stderr {
|
2011 |
|
|
set data {}
|
2012 |
|
|
foreach {first last} [$PRIV(console) tag ranges $type] {
|
2013 |
|
|
lappend data [$PRIV(console) get $first $last]
|
2014 |
|
|
}
|
2015 |
|
|
set data [join $data \n]
|
2016 |
|
|
}
|
2017 |
|
|
history { set data [tkcon history] }
|
2018 |
|
|
all - default { set data [$PRIV(console) get 1.0 end-1c] }
|
2019 |
|
|
widget {
|
2020 |
|
|
set data [$opt get 1.0 end-1c]
|
2021 |
|
|
}
|
2022 |
|
|
}
|
2023 |
|
|
if {[catch {open $fn $mode} fid]} {
|
2024 |
|
|
return -code error "Save Error: Unable to open '$fn' for writing\n$fid"
|
2025 |
|
|
}
|
2026 |
|
|
puts -nonewline $fid $data
|
2027 |
|
|
close $fid
|
2028 |
|
|
}
|
2029 |
|
|
|
2030 |
|
|
## ::tkcon::MainInit
|
2031 |
|
|
## This is only called for the main interpreter to include certain procs
|
2032 |
|
|
## that we don't want to include (or rather, just alias) in slave interps.
|
2033 |
|
|
##
|
2034 |
|
|
proc ::tkcon::MainInit {} {
|
2035 |
|
|
variable PRIV
|
2036 |
|
|
|
2037 |
|
|
if {![info exists PRIV(slaves)]} {
|
2038 |
|
|
array set PRIV [list slave 0 slaves Main name {} \
|
2039 |
|
|
interps [list [tk appname]]]
|
2040 |
|
|
}
|
2041 |
|
|
interp alias {} ::tkcon::Main {} ::tkcon::InterpEval Main
|
2042 |
|
|
interp alias {} ::tkcon::Slave {} ::tkcon::InterpEval
|
2043 |
|
|
|
2044 |
|
|
proc ::tkcon::GetSlaveNum {} {
|
2045 |
|
|
set i -1
|
2046 |
|
|
while {[interp exists Slave[incr i]]} {
|
2047 |
|
|
# oh my god, an empty loop!
|
2048 |
|
|
}
|
2049 |
|
|
return $i
|
2050 |
|
|
}
|
2051 |
|
|
|
2052 |
|
|
## ::tkcon::New - create new console window
|
2053 |
|
|
## Creates a slave interpreter and sources in this script.
|
2054 |
|
|
## All other interpreters also get a command to eval function in the
|
2055 |
|
|
## new interpreter.
|
2056 |
|
|
##
|
2057 |
|
|
proc ::tkcon::New {} {
|
2058 |
|
|
variable PRIV
|
2059 |
|
|
global argv0 argc argv
|
2060 |
|
|
|
2061 |
|
|
set tmp [interp create Slave[GetSlaveNum]]
|
2062 |
|
|
lappend PRIV(slaves) $tmp
|
2063 |
|
|
load {} Tk $tmp
|
2064 |
|
|
lappend PRIV(interps) [$tmp eval [list tk appname \
|
2065 |
|
|
"[tk appname] $tmp"]]
|
2066 |
|
|
if {[info exist argv0]} {$tmp eval [list set argv0 $argv0]}
|
2067 |
|
|
$tmp eval set argc $argc
|
2068 |
|
|
$tmp eval [list set argv $argv]
|
2069 |
|
|
$tmp eval [list namespace eval ::tkcon {}]
|
2070 |
|
|
$tmp eval [list set ::tkcon::PRIV(name) $tmp]
|
2071 |
|
|
$tmp eval [list set ::tkcon::PRIV(SCRIPT) $::tkcon::PRIV(SCRIPT)]
|
2072 |
|
|
$tmp alias exit ::tkcon::Exit $tmp
|
2073 |
|
|
$tmp alias ::tkcon::Destroy ::tkcon::Destroy $tmp
|
2074 |
|
|
$tmp alias ::tkcon::New ::tkcon::New
|
2075 |
|
|
$tmp alias ::tkcon::Main ::tkcon::InterpEval Main
|
2076 |
|
|
$tmp alias ::tkcon::Slave ::tkcon::InterpEval
|
2077 |
|
|
$tmp alias ::tkcon::Interps ::tkcon::Interps
|
2078 |
|
|
$tmp alias ::tkcon::NewDisplay ::tkcon::NewDisplay
|
2079 |
|
|
$tmp alias ::tkcon::Display ::tkcon::Display
|
2080 |
|
|
$tmp alias ::tkcon::StateCheckpoint ::tkcon::StateCheckpoint
|
2081 |
|
|
$tmp alias ::tkcon::StateCleanup ::tkcon::StateCleanup
|
2082 |
|
|
$tmp alias ::tkcon::StateCompare ::tkcon::StateCompare
|
2083 |
|
|
$tmp alias ::tkcon::StateRevert ::tkcon::StateRevert
|
2084 |
|
|
$tmp eval {
|
2085 |
|
|
if [catch {source -rsrc tkcon}] { source $::tkcon::PRIV(SCRIPT) }
|
2086 |
|
|
}
|
2087 |
|
|
return $tmp
|
2088 |
|
|
}
|
2089 |
|
|
|
2090 |
|
|
## ::tkcon::Exit - full exit OR destroy slave console
|
2091 |
|
|
## This proc should only be called in the main interpreter from a slave.
|
2092 |
|
|
## The master determines whether we do a full exit or just kill the slave.
|
2093 |
|
|
##
|
2094 |
|
|
proc ::tkcon::Exit {slave args} {
|
2095 |
|
|
variable PRIV
|
2096 |
|
|
variable OPT
|
2097 |
|
|
|
2098 |
|
|
## Slave interpreter exit request
|
2099 |
|
|
if {[string match exit $OPT(slaveexit)]} {
|
2100 |
|
|
## Only exit if it specifically is stated to do so
|
2101 |
|
|
uplevel 1 exit $args
|
2102 |
|
|
}
|
2103 |
|
|
## Otherwise we will delete the slave interp and associated data
|
2104 |
|
|
set name [InterpEval $slave]
|
2105 |
|
|
set PRIV(interps) [lremove $PRIV(interps) [list $name]]
|
2106 |
|
|
set PRIV(slaves) [lremove $PRIV(slaves) [list $slave]]
|
2107 |
|
|
interp delete $slave
|
2108 |
|
|
StateCleanup $slave
|
2109 |
|
|
return
|
2110 |
|
|
}
|
2111 |
|
|
|
2112 |
|
|
## ::tkcon::Destroy - destroy console window
|
2113 |
|
|
## This proc should only be called by the main interpreter. If it is
|
2114 |
|
|
## called from there, it will ask before exiting tkcon. All others
|
2115 |
|
|
## (slaves) will just have their slave interpreter deleted, closing them.
|
2116 |
|
|
##
|
2117 |
|
|
proc ::tkcon::Destroy {{slave {}}} {
|
2118 |
|
|
variable PRIV
|
2119 |
|
|
|
2120 |
|
|
if {[string match {} $slave]} {
|
2121 |
|
|
## Main interpreter close request
|
2122 |
|
|
if {[tk_dialog $PRIV(base).destroyme {Quit tkcon?} \
|
2123 |
|
|
{Closing the Main console will quit tkcon} \
|
2124 |
|
|
warning 0 "Don't Quit" "Quit tkcon"]} exit
|
2125 |
|
|
} else {
|
2126 |
|
|
## Slave interpreter close request
|
2127 |
|
|
set name [InterpEval $slave]
|
2128 |
|
|
set PRIV(interps) [lremove $PRIV(interps) [list $name]]
|
2129 |
|
|
set PRIV(slaves) [lremove $PRIV(slaves) [list $slave]]
|
2130 |
|
|
interp delete $slave
|
2131 |
|
|
}
|
2132 |
|
|
StateCleanup $slave
|
2133 |
|
|
return
|
2134 |
|
|
}
|
2135 |
|
|
|
2136 |
|
|
## We want to do a couple things before exiting...
|
2137 |
|
|
if {[catch {rename ::exit ::tkcon::FinalExit} err]} {
|
2138 |
|
|
puts stderr "tkcon might panic:\n$err"
|
2139 |
|
|
}
|
2140 |
|
|
proc ::exit args {
|
2141 |
|
|
if {$::tkcon::OPT(usehistory)} {
|
2142 |
|
|
if {[catch {open $::tkcon::PRIV(histfile) w} fid]} {
|
2143 |
|
|
puts stderr "unable to save history file:\n$fid"
|
2144 |
|
|
# pause a moment, because we are about to die finally...
|
2145 |
|
|
after 1000
|
2146 |
|
|
} else {
|
2147 |
|
|
set max [::tkcon::EvalSlave history nextid]
|
2148 |
|
|
set id [expr {$max - $::tkcon::OPT(history)}]
|
2149 |
|
|
if {$id < 1} { set id 1 }
|
2150 |
|
|
## FIX: This puts history in backwards!!
|
2151 |
|
|
while {($id < $max) && \
|
2152 |
|
|
![catch {::tkcon::EvalSlave history event $id} cmd]} {
|
2153 |
|
|
if {[string compare {} $cmd]} {
|
2154 |
|
|
puts $fid "::tkcon::EvalSlave history add [list $cmd]"
|
2155 |
|
|
}
|
2156 |
|
|
incr id
|
2157 |
|
|
}
|
2158 |
|
|
close $fid
|
2159 |
|
|
}
|
2160 |
|
|
}
|
2161 |
|
|
uplevel 1 ::tkcon::FinalExit $args
|
2162 |
|
|
}
|
2163 |
|
|
|
2164 |
|
|
## ::tkcon::InterpEval - passes evaluation to another named interpreter
|
2165 |
|
|
## If the interpreter is named, but no args are given, it returns the
|
2166 |
|
|
## [tk appname] of that interps master (not the associated eval slave).
|
2167 |
|
|
##
|
2168 |
|
|
proc ::tkcon::InterpEval {{slave {}} args} {
|
2169 |
|
|
variable PRIV
|
2170 |
|
|
|
2171 |
|
|
if {[string match {} $slave]} {
|
2172 |
|
|
return $PRIV(slaves)
|
2173 |
|
|
} elseif {[string match {[Mm]ain} $slave]} {
|
2174 |
|
|
set slave {}
|
2175 |
|
|
}
|
2176 |
|
|
if {[llength $args]} {
|
2177 |
|
|
return [interp eval $slave uplevel \#0 $args]
|
2178 |
|
|
} else {
|
2179 |
|
|
return [interp eval $slave tk appname]
|
2180 |
|
|
}
|
2181 |
|
|
}
|
2182 |
|
|
|
2183 |
|
|
proc ::tkcon::Interps {{ls {}} {interp {}}} {
|
2184 |
|
|
if {[string match {} $interp]} { lappend ls {} [tk appname] }
|
2185 |
|
|
foreach i [interp slaves $interp] {
|
2186 |
|
|
if {[string compare {} $interp]} { set i "$interp $i" }
|
2187 |
|
|
if {[string compare {} [interp eval $i package provide Tk]]} {
|
2188 |
|
|
lappend ls $i [interp eval $i tk appname]
|
2189 |
|
|
} else {
|
2190 |
|
|
lappend ls $i {}
|
2191 |
|
|
}
|
2192 |
|
|
set ls [Interps $ls $i]
|
2193 |
|
|
}
|
2194 |
|
|
return $ls
|
2195 |
|
|
}
|
2196 |
|
|
|
2197 |
|
|
proc ::tkcon::Display {{disp {}}} {
|
2198 |
|
|
variable DISP
|
2199 |
|
|
|
2200 |
|
|
set res {}
|
2201 |
|
|
if {$disp != ""} {
|
2202 |
|
|
if {![info exists DISP($disp)]} { return }
|
2203 |
|
|
return [list $DISP($disp) [winfo interps -displayof $DISP($disp)]]
|
2204 |
|
|
}
|
2205 |
|
|
return [lsort -dictionary [array names DISP]]
|
2206 |
|
|
}
|
2207 |
|
|
|
2208 |
|
|
proc ::tkcon::NewDisplay {} {
|
2209 |
|
|
variable PRIV
|
2210 |
|
|
variable DISP
|
2211 |
|
|
|
2212 |
|
|
set t $PRIV(base).newdisp
|
2213 |
|
|
if {![winfo exists $t]} {
|
2214 |
|
|
toplevel $t
|
2215 |
|
|
wm withdraw $t
|
2216 |
|
|
wm title $t "tkcon Attach to Display"
|
2217 |
|
|
label $t.gets -text "New Display: "
|
2218 |
|
|
entry $t.data -width 32
|
2219 |
|
|
button $t.ok -text "OK" -command {set ::tkcon::PRIV(grab) 1}
|
2220 |
|
|
bind $t.data <Return> [list $t.ok invoke]
|
2221 |
|
|
bind $t.ok <Return> [list $t.ok invoke]
|
2222 |
|
|
grid $t.gets $t.data -sticky ew
|
2223 |
|
|
grid $t.ok - -sticky ew
|
2224 |
|
|
grid columnconfig $t 1 -weight 1
|
2225 |
|
|
grid rowconfigure $t 1 -weight 1
|
2226 |
|
|
wm transient $t $PRIV(root)
|
2227 |
|
|
wm geometry $t +[expr {([winfo screenwidth $t]-[winfo \
|
2228 |
|
|
reqwidth $t]) / 2}]+[expr {([winfo \
|
2229 |
|
|
screenheight $t]-[winfo reqheight $t]) / 2}]
|
2230 |
|
|
}
|
2231 |
|
|
$t.data delete 0 end
|
2232 |
|
|
wm deiconify $t
|
2233 |
|
|
raise $t
|
2234 |
|
|
grab $t
|
2235 |
|
|
focus $t.data
|
2236 |
|
|
vwait ::tkcon::PRIV(grab)
|
2237 |
|
|
grab release $t
|
2238 |
|
|
wm withdraw $t
|
2239 |
|
|
set disp [$t.data get]
|
2240 |
|
|
if {$disp == ""} { return }
|
2241 |
|
|
regsub -all {\.} [string tolower $disp] ! dt
|
2242 |
|
|
set dt $PRIV(base).$dt
|
2243 |
|
|
destroy $dt
|
2244 |
|
|
if {[catch {
|
2245 |
|
|
toplevel $dt -screen $disp
|
2246 |
|
|
set interps [winfo interps -displayof $dt]
|
2247 |
|
|
if {![llength $interps]} {
|
2248 |
|
|
error "No other Tk interpreters on $disp"
|
2249 |
|
|
}
|
2250 |
|
|
send -displayof $dt [lindex $interps 0] [list info tclversion]
|
2251 |
|
|
} err]} {
|
2252 |
|
|
global env
|
2253 |
|
|
if {[info exists env(DISPLAY)]} {
|
2254 |
|
|
set myd $env(DISPLAY)
|
2255 |
|
|
} else {
|
2256 |
|
|
set myd "myDisplay:0"
|
2257 |
|
|
}
|
2258 |
|
|
tk_messageBox -title "Display Connection Error" \
|
2259 |
|
|
-message "Unable to connect to \"$disp\":\n$err\
|
2260 |
|
|
\nMake sure you have xauth-based permissions\
|
2261 |
|
|
(xauth add $myd . `mcookie`), and xhost is disabled\
|
2262 |
|
|
(xhost -) on \"$disp\"" \
|
2263 |
|
|
-icon error -type ok
|
2264 |
|
|
destroy $dt
|
2265 |
|
|
return
|
2266 |
|
|
}
|
2267 |
|
|
set DISP($disp) $dt
|
2268 |
|
|
wm withdraw $dt
|
2269 |
|
|
bind $dt <Destroy> [subst {catch {unset ::tkcon::DISP($disp)}}]
|
2270 |
|
|
tk_messageBox -title "$disp Connection" \
|
2271 |
|
|
-message "Connected to \"$disp\", found:\n[join $interps \n]" \
|
2272 |
|
|
-type ok
|
2273 |
|
|
}
|
2274 |
|
|
|
2275 |
|
|
##
|
2276 |
|
|
## The following state checkpoint/revert procedures are very sketchy
|
2277 |
|
|
## and prone to problems. They do not track modifications to currently
|
2278 |
|
|
## existing procedures/variables, and they can really screw things up
|
2279 |
|
|
## if you load in libraries (especially Tk) between checkpoint and
|
2280 |
|
|
## revert. Only with this knowledge in mind should you use these.
|
2281 |
|
|
##
|
2282 |
|
|
|
2283 |
|
|
## ::tkcon::StateCheckpoint - checkpoints the current state of the system
|
2284 |
|
|
## This allows you to return to this state with ::tkcon::StateRevert
|
2285 |
|
|
# ARGS:
|
2286 |
|
|
##
|
2287 |
|
|
proc ::tkcon::StateCheckpoint {app type} {
|
2288 |
|
|
variable CPS
|
2289 |
|
|
variable PRIV
|
2290 |
|
|
|
2291 |
|
|
if {[info exists CPS($type,$app,cmd)] && \
|
2292 |
|
|
[tk_dialog $PRIV(base).warning "Overwrite Previous State?" \
|
2293 |
|
|
"Are you sure you want to lose previously checkpointed\
|
2294 |
|
|
state of $type \"$app\"?" questhead 1 "Do It" "Cancel"]} return
|
2295 |
|
|
set CPS($type,$app,cmd) [EvalOther $app $type info commands *]
|
2296 |
|
|
set CPS($type,$app,var) [EvalOther $app $type info vars *]
|
2297 |
|
|
return
|
2298 |
|
|
}
|
2299 |
|
|
|
2300 |
|
|
## ::tkcon::StateCompare - compare two states and output difference
|
2301 |
|
|
# ARGS:
|
2302 |
|
|
##
|
2303 |
|
|
proc ::tkcon::StateCompare {app type {verbose 0}} {
|
2304 |
|
|
variable CPS
|
2305 |
|
|
variable PRIV
|
2306 |
|
|
variable OPT
|
2307 |
|
|
variable COLOR
|
2308 |
|
|
|
2309 |
|
|
if {![info exists CPS($type,$app,cmd)]} {
|
2310 |
|
|
return -code error \
|
2311 |
|
|
"No previously checkpointed state for $type \"$app\""
|
2312 |
|
|
}
|
2313 |
|
|
set w $PRIV(base).compare
|
2314 |
|
|
if {[winfo exists $w]} {
|
2315 |
|
|
$w.text config -state normal
|
2316 |
|
|
$w.text delete 1.0 end
|
2317 |
|
|
} else {
|
2318 |
|
|
toplevel $w
|
2319 |
|
|
frame $w.btn
|
2320 |
|
|
scrollbar $w.sy -takefocus 0 -bd 1 -command [list $w.text yview]
|
2321 |
|
|
text $w.text -yscrollcommand [list $w.sy set] -height 12 \
|
2322 |
|
|
-foreground $COLOR(stdin) \
|
2323 |
|
|
-background $COLOR(bg) \
|
2324 |
|
|
-insertbackground $COLOR(cursor) \
|
2325 |
|
|
-font $OPT(font)
|
2326 |
|
|
pack $w.btn -side bottom -fill x
|
2327 |
|
|
pack $w.sy -side right -fill y
|
2328 |
|
|
pack $w.text -fill both -expand 1
|
2329 |
|
|
button $w.btn.close -text "Dismiss" -width 11 \
|
2330 |
|
|
-command [list destroy $w]
|
2331 |
|
|
button $w.btn.check -text "Recheckpoint" -width 11
|
2332 |
|
|
button $w.btn.revert -text "Revert" -width 11
|
2333 |
|
|
button $w.btn.expand -text "Verbose" -width 11
|
2334 |
|
|
button $w.btn.update -text "Update" -width 11
|
2335 |
|
|
pack $w.btn.check $w.btn.revert $w.btn.expand $w.btn.update \
|
2336 |
|
|
$w.btn.close -side left -fill x -padx 4 -pady 2 -expand 1
|
2337 |
|
|
$w.text tag config red -foreground red
|
2338 |
|
|
}
|
2339 |
|
|
wm title $w "Compare State: $type [list $app]"
|
2340 |
|
|
|
2341 |
|
|
$w.btn.check config \
|
2342 |
|
|
-command "::tkcon::StateCheckpoint [list $app] $type; \
|
2343 |
|
|
::tkcon::StateCompare [list $app] $type $verbose"
|
2344 |
|
|
$w.btn.revert config \
|
2345 |
|
|
-command "::tkcon::StateRevert [list $app] $type; \
|
2346 |
|
|
::tkcon::StateCompare [list $app] $type $verbose"
|
2347 |
|
|
$w.btn.update config -command [info level 0]
|
2348 |
|
|
if {$verbose} {
|
2349 |
|
|
$w.btn.expand config -text Brief \
|
2350 |
|
|
-command [list ::tkcon::StateCompare $app $type 0]
|
2351 |
|
|
} else {
|
2352 |
|
|
$w.btn.expand config -text Verbose \
|
2353 |
|
|
-command [list ::tkcon::StateCompare $app $type 1]
|
2354 |
|
|
}
|
2355 |
|
|
## Don't allow verbose mode unless 'dump' exists in $app
|
2356 |
|
|
## We're assuming this is tkcon's dump command
|
2357 |
|
|
set hasdump [llength [EvalOther $app $type info commands dump]]
|
2358 |
|
|
if {$hasdump} {
|
2359 |
|
|
$w.btn.expand config -state normal
|
2360 |
|
|
} else {
|
2361 |
|
|
$w.btn.expand config -state disabled
|
2362 |
|
|
}
|
2363 |
|
|
|
2364 |
|
|
set cmds [lremove [EvalOther $app $type info commands *] \
|
2365 |
|
|
$CPS($type,$app,cmd)]
|
2366 |
|
|
set vars [lremove [EvalOther $app $type info vars *] \
|
2367 |
|
|
$CPS($type,$app,var)]
|
2368 |
|
|
|
2369 |
|
|
if {$hasdump && $verbose} {
|
2370 |
|
|
set cmds [EvalOther $app $type eval dump c -nocomplain $cmds]
|
2371 |
|
|
set vars [EvalOther $app $type eval dump v -nocomplain $vars]
|
2372 |
|
|
}
|
2373 |
|
|
$w.text insert 1.0 "NEW COMMANDS IN \"$app\":\n" red \
|
2374 |
|
|
$cmds {} "\n\nNEW VARIABLES IN \"$app\":\n" red $vars {}
|
2375 |
|
|
|
2376 |
|
|
raise $w
|
2377 |
|
|
$w.text config -state disabled
|
2378 |
|
|
}
|
2379 |
|
|
|
2380 |
|
|
## ::tkcon::StateRevert - reverts interpreter to previous state
|
2381 |
|
|
# ARGS:
|
2382 |
|
|
##
|
2383 |
|
|
proc ::tkcon::StateRevert {app type} {
|
2384 |
|
|
variable CPS
|
2385 |
|
|
variable PRIV
|
2386 |
|
|
|
2387 |
|
|
if {![info exists CPS($type,$app,cmd)]} {
|
2388 |
|
|
return -code error \
|
2389 |
|
|
"No previously checkpointed state for $type \"$app\""
|
2390 |
|
|
}
|
2391 |
|
|
if {![tk_dialog $PRIV(base).warning "Revert State?" \
|
2392 |
|
|
"Are you sure you want to revert the state in $type \"$app\"?"\
|
2393 |
|
|
questhead 1 "Do It" "Cancel"]} {
|
2394 |
|
|
foreach i [lremove [EvalOther $app $type info commands *] \
|
2395 |
|
|
$CPS($type,$app,cmd)] {
|
2396 |
|
|
catch {EvalOther $app $type rename $i {}}
|
2397 |
|
|
}
|
2398 |
|
|
foreach i [lremove [EvalOther $app $type info vars *] \
|
2399 |
|
|
$CPS($type,$app,var)] {
|
2400 |
|
|
catch {EvalOther $app $type unset $i}
|
2401 |
|
|
}
|
2402 |
|
|
}
|
2403 |
|
|
}
|
2404 |
|
|
|
2405 |
|
|
## ::tkcon::StateCleanup - cleans up state information in master array
|
2406 |
|
|
#
|
2407 |
|
|
##
|
2408 |
|
|
proc ::tkcon::StateCleanup {args} {
|
2409 |
|
|
variable CPS
|
2410 |
|
|
|
2411 |
|
|
if {![llength $args]} {
|
2412 |
|
|
foreach state [array names CPS slave,*] {
|
2413 |
|
|
if {![interp exists [string range $state 6 end]]} {
|
2414 |
|
|
unset CPS($state)
|
2415 |
|
|
}
|
2416 |
|
|
}
|
2417 |
|
|
} else {
|
2418 |
|
|
set app [lindex $args 0]
|
2419 |
|
|
set type [lindex $args 1]
|
2420 |
|
|
if {[regexp {^(|slave)$} $type]} {
|
2421 |
|
|
foreach state [array names CPS "slave,$app\[, \]*"] {
|
2422 |
|
|
if {![interp exists [string range $state 6 end]]} {
|
2423 |
|
|
unset CPS($state)
|
2424 |
|
|
}
|
2425 |
|
|
}
|
2426 |
|
|
} else {
|
2427 |
|
|
catch {unset CPS($type,$app)}
|
2428 |
|
|
}
|
2429 |
|
|
}
|
2430 |
|
|
}
|
2431 |
|
|
}
|
2432 |
|
|
|
2433 |
|
|
## ::tkcon::Event - get history event, search if string != {}
|
2434 |
|
|
## look forward (next) if $int>0, otherwise look back (prev)
|
2435 |
|
|
# ARGS: W - console widget
|
2436 |
|
|
##
|
2437 |
|
|
proc ::tkcon::Event {int {str {}}} {
|
2438 |
|
|
if {!$int} return
|
2439 |
|
|
|
2440 |
|
|
variable PRIV
|
2441 |
|
|
set w $PRIV(console)
|
2442 |
|
|
|
2443 |
|
|
set nextid [EvalSlave history nextid]
|
2444 |
|
|
if {[string compare {} $str]} {
|
2445 |
|
|
## String is not empty, do an event search
|
2446 |
|
|
set event $PRIV(event)
|
2447 |
|
|
if {$int < 0 && $event == $nextid} { set PRIV(cmdbuf) $str }
|
2448 |
|
|
set len [string len $PRIV(cmdbuf)]
|
2449 |
|
|
incr len -1
|
2450 |
|
|
if {$int > 0} {
|
2451 |
|
|
## Search history forward
|
2452 |
|
|
while {$event < $nextid} {
|
2453 |
|
|
if {[incr event] == $nextid} {
|
2454 |
|
|
$w delete limit end
|
2455 |
|
|
$w insert limit $PRIV(cmdbuf)
|
2456 |
|
|
break
|
2457 |
|
|
} elseif {
|
2458 |
|
|
![catch {EvalSlave history event $event} res] &&
|
2459 |
|
|
[set p [string first $PRIV(cmdbuf) $res]] > -1
|
2460 |
|
|
} {
|
2461 |
|
|
set p2 [expr {$p + [string length $PRIV(cmdbuf)]}]
|
2462 |
|
|
$w delete limit end
|
2463 |
|
|
$w insert limit $res
|
2464 |
|
|
Blink $w "limit + $p c" "limit + $p2 c"
|
2465 |
|
|
break
|
2466 |
|
|
}
|
2467 |
|
|
}
|
2468 |
|
|
set PRIV(event) $event
|
2469 |
|
|
} else {
|
2470 |
|
|
## Search history reverse
|
2471 |
|
|
while {![catch {EvalSlave history event [incr event -1]} res]} {
|
2472 |
|
|
if {[set p [string first $PRIV(cmdbuf) $res]] > -1} {
|
2473 |
|
|
set p2 [expr {$p + [string length $PRIV(cmdbuf)]}]
|
2474 |
|
|
$w delete limit end
|
2475 |
|
|
$w insert limit $res
|
2476 |
|
|
set PRIV(event) $event
|
2477 |
|
|
Blink $w "limit + $p c" "limit + $p2 c"
|
2478 |
|
|
break
|
2479 |
|
|
}
|
2480 |
|
|
}
|
2481 |
|
|
}
|
2482 |
|
|
} else {
|
2483 |
|
|
## String is empty, just get next/prev event
|
2484 |
|
|
if {$int > 0} {
|
2485 |
|
|
## Goto next command in history
|
2486 |
|
|
if {$PRIV(event) < $nextid} {
|
2487 |
|
|
$w delete limit end
|
2488 |
|
|
if {[incr PRIV(event)] == $nextid} {
|
2489 |
|
|
$w insert limit $PRIV(cmdbuf)
|
2490 |
|
|
} else {
|
2491 |
|
|
$w insert limit [EvalSlave history event $PRIV(event)]
|
2492 |
|
|
}
|
2493 |
|
|
}
|
2494 |
|
|
} else {
|
2495 |
|
|
## Goto previous command in history
|
2496 |
|
|
if {$PRIV(event) == $nextid} {
|
2497 |
|
|
set PRIV(cmdbuf) [CmdGet $w]
|
2498 |
|
|
}
|
2499 |
|
|
if {[catch {EvalSlave history event [incr PRIV(event) -1]} res]} {
|
2500 |
|
|
incr PRIV(event)
|
2501 |
|
|
} else {
|
2502 |
|
|
$w delete limit end
|
2503 |
|
|
$w insert limit $res
|
2504 |
|
|
}
|
2505 |
|
|
}
|
2506 |
|
|
}
|
2507 |
|
|
$w mark set insert end
|
2508 |
|
|
$w see end
|
2509 |
|
|
}
|
2510 |
|
|
|
2511 |
|
|
## ::tkcon::ErrorHighlight - magic error highlighting
|
2512 |
|
|
## beware: voodoo included
|
2513 |
|
|
# ARGS:
|
2514 |
|
|
##
|
2515 |
|
|
proc ::tkcon::ErrorHighlight w {
|
2516 |
|
|
variable COLOR
|
2517 |
|
|
|
2518 |
|
|
## do voodoo here
|
2519 |
|
|
set app [Attach]
|
2520 |
|
|
# we have to pull the text out, because text regexps are screwed on \n's.
|
2521 |
|
|
set info [$w get 1.0 end-1c]
|
2522 |
|
|
# Check for specific line error in a proc
|
2523 |
|
|
set exp(proc) "\"(\[^\"\]+)\"\n\[\t \]+\\\(procedure \"(\[^\"\]+)\""
|
2524 |
|
|
# Check for too few args to a proc
|
2525 |
|
|
set exp(param) "parameter \"(\[^\"\]+)\" to \"(\[^\"\]+)\""
|
2526 |
|
|
set start 1.0
|
2527 |
|
|
while {
|
2528 |
|
|
[regexp -indices -- $exp(proc) $info junk what cmd] ||
|
2529 |
|
|
[regexp -indices -- $exp(param) $info junk what cmd]
|
2530 |
|
|
} {
|
2531 |
|
|
foreach {w0 w1} $what {c0 c1} $cmd {break}
|
2532 |
|
|
set what [string range $info $w0 $w1]
|
2533 |
|
|
set cmd [string range $info $c0 $c1]
|
2534 |
|
|
if {[string match *::* $cmd]} {
|
2535 |
|
|
set res [uplevel 1 ::tkcon::EvalOther $app namespace eval \
|
2536 |
|
|
[list [namespace qualifiers $cmd] \
|
2537 |
|
|
[list info procs [namespace tail $cmd]]]]
|
2538 |
|
|
} else {
|
2539 |
|
|
set res [uplevel 1 ::tkcon::EvalOther $app info procs [list $cmd]]
|
2540 |
|
|
}
|
2541 |
|
|
if {[llength $res]==1} {
|
2542 |
|
|
set tag [UniqueTag $w]
|
2543 |
|
|
$w tag add $tag $start+${c0}c $start+1c+${c1}c
|
2544 |
|
|
$w tag configure $tag -foreground $COLOR(stdout)
|
2545 |
|
|
$w tag bind $tag <Enter> [list $w tag configure $tag -under 1]
|
2546 |
|
|
$w tag bind $tag <Leave> [list $w tag configure $tag -under 0]
|
2547 |
|
|
$w tag bind $tag <ButtonRelease-1> "if {!\$tkPriv(mouseMoved)} \
|
2548 |
|
|
{[list edit -attach $app -type proc -find $what -- $cmd]}"
|
2549 |
|
|
}
|
2550 |
|
|
set info [string range $info $c1 end]
|
2551 |
|
|
set start [$w index $start+${c1}c]
|
2552 |
|
|
}
|
2553 |
|
|
## Next stage, check for procs that start a line
|
2554 |
|
|
set start 1.0
|
2555 |
|
|
set exp(cmd) "^\"\[^\" \t\n\]+"
|
2556 |
|
|
while {
|
2557 |
|
|
[string compare {} [set ix \
|
2558 |
|
|
[$w search -regexp -count numc -- $exp(cmd) $start end]]]
|
2559 |
|
|
} {
|
2560 |
|
|
set start [$w index $ix+${numc}c]
|
2561 |
|
|
# +1c to avoid the first quote
|
2562 |
|
|
set cmd [$w get $ix+1c $start]
|
2563 |
|
|
if {[string match *::* $cmd]} {
|
2564 |
|
|
set res [uplevel 1 ::tkcon::EvalOther $app namespace eval \
|
2565 |
|
|
[list [namespace qualifiers $cmd] \
|
2566 |
|
|
[list info procs [namespace tail $cmd]]]]
|
2567 |
|
|
} else {
|
2568 |
|
|
set res [uplevel 1 ::tkcon::EvalOther $app info procs [list $cmd]]
|
2569 |
|
|
}
|
2570 |
|
|
if {[llength $res]==1} {
|
2571 |
|
|
set tag [UniqueTag $w]
|
2572 |
|
|
$w tag add $tag $ix+1c $start
|
2573 |
|
|
$w tag configure $tag -foreground $COLOR(proc)
|
2574 |
|
|
$w tag bind $tag <Enter> [list $w tag configure $tag -under 1]
|
2575 |
|
|
$w tag bind $tag <Leave> [list $w tag configure $tag -under 0]
|
2576 |
|
|
$w tag bind $tag <ButtonRelease-1> "if {!\$tkPriv(mouseMoved)} \
|
2577 |
|
|
{[list edit -attach $app -type proc -- $cmd]}"
|
2578 |
|
|
}
|
2579 |
|
|
}
|
2580 |
|
|
}
|
2581 |
|
|
|
2582 |
|
|
## tkcon - command that allows control over the console
|
2583 |
|
|
## This always exists in the main interpreter, and is aliased into
|
2584 |
|
|
## other connected interpreters
|
2585 |
|
|
# ARGS: totally variable, see internal comments
|
2586 |
|
|
##
|
2587 |
|
|
proc tkcon {cmd args} {
|
2588 |
|
|
global errorInfo
|
2589 |
|
|
|
2590 |
|
|
switch -glob -- $cmd {
|
2591 |
|
|
buf* {
|
2592 |
|
|
## 'buffer' Sets/Query the buffer size
|
2593 |
|
|
if {[llength $args]} {
|
2594 |
|
|
if {[regexp {^[1-9][0-9]*$} $args]} {
|
2595 |
|
|
set ::tkcon::OPT(buffer) $args
|
2596 |
|
|
# catch in case the console doesn't exist yet
|
2597 |
|
|
catch {::tkcon::ConstrainBuffer $::tkcon::PRIV(console) \
|
2598 |
|
|
$::tkcon::OPT(buffer)}
|
2599 |
|
|
} else {
|
2600 |
|
|
return -code error "buffer must be a valid integer"
|
2601 |
|
|
}
|
2602 |
|
|
}
|
2603 |
|
|
return $::tkcon::OPT(buffer)
|
2604 |
|
|
}
|
2605 |
|
|
bg* {
|
2606 |
|
|
## 'bgerror' Brings up an error dialog
|
2607 |
|
|
set errorInfo [lindex $args 1]
|
2608 |
|
|
bgerror [lindex $args 0]
|
2609 |
|
|
}
|
2610 |
|
|
cl* {
|
2611 |
|
|
## 'close' Closes the console
|
2612 |
|
|
::tkcon::Destroy
|
2613 |
|
|
}
|
2614 |
|
|
cons* {
|
2615 |
|
|
## 'console' - passes the args to the text widget of the console.
|
2616 |
|
|
set result [uplevel 1 $::tkcon::PRIV(console) $args]
|
2617 |
|
|
::tkcon::ConstrainBuffer $::tkcon::PRIV(console) \
|
2618 |
|
|
$::tkcon::OPT(buffer)
|
2619 |
|
|
return $result
|
2620 |
|
|
}
|
2621 |
|
|
congets {
|
2622 |
|
|
## 'congets' a replacement for [gets stdin]
|
2623 |
|
|
# Use the 'gets' alias of 'tkcon_gets' command instead of
|
2624 |
|
|
# calling the *get* methods directly for best compatability
|
2625 |
|
|
if {[llength $args]} {
|
2626 |
|
|
return -code error "wrong # args: must be \"tkcon congets\""
|
2627 |
|
|
}
|
2628 |
|
|
tkcon show
|
2629 |
|
|
set old [bind TkConsole <<TkCon_Eval>>]
|
2630 |
|
|
bind TkConsole <<TkCon_Eval>> { set ::tkcon::PRIV(wait) 0 }
|
2631 |
|
|
set w $::tkcon::PRIV(console)
|
2632 |
|
|
# Make sure to move the limit to get the right data
|
2633 |
|
|
$w mark set insert end
|
2634 |
|
|
$w mark set limit insert
|
2635 |
|
|
$w see end
|
2636 |
|
|
vwait ::tkcon::PRIV(wait)
|
2637 |
|
|
set line [::tkcon::CmdGet $w]
|
2638 |
|
|
$w insert end \n
|
2639 |
|
|
bind TkConsole <<TkCon_Eval>> $old
|
2640 |
|
|
return $line
|
2641 |
|
|
}
|
2642 |
|
|
getc* {
|
2643 |
|
|
## 'getcommand' a replacement for [gets stdin]
|
2644 |
|
|
## This forces a complete command to be input though
|
2645 |
|
|
if {[llength $args]} {
|
2646 |
|
|
return -code error "wrong # args: must be \"tkcon getcommand\""
|
2647 |
|
|
}
|
2648 |
|
|
tkcon show
|
2649 |
|
|
set old [bind TkConsole <<TkCon_Eval>>]
|
2650 |
|
|
bind TkConsole <<TkCon_Eval>> { set ::tkcon::PRIV(wait) 0 }
|
2651 |
|
|
set w $::tkcon::PRIV(console)
|
2652 |
|
|
# Make sure to move the limit to get the right data
|
2653 |
|
|
$w mark set insert end
|
2654 |
|
|
$w mark set limit insert
|
2655 |
|
|
$w see end
|
2656 |
|
|
vwait ::tkcon::PRIV(wait)
|
2657 |
|
|
set line [::tkcon::CmdGet $w]
|
2658 |
|
|
$w insert end \n
|
2659 |
|
|
while {![info complete $line] || [regexp {[^\\]\\$} $line]} {
|
2660 |
|
|
vwait ::tkcon::PRIV(wait)
|
2661 |
|
|
set line [::tkcon::CmdGet $w]
|
2662 |
|
|
$w insert end \n
|
2663 |
|
|
$w see end
|
2664 |
|
|
}
|
2665 |
|
|
bind TkConsole <<TkCon_Eval>> $old
|
2666 |
|
|
return $line
|
2667 |
|
|
}
|
2668 |
|
|
get - gets {
|
2669 |
|
|
## 'gets' - a replacement for [gets stdin]
|
2670 |
|
|
## This pops up a text widget to be used for stdin (local grabbed)
|
2671 |
|
|
if {[llength $args]} {
|
2672 |
|
|
return -code error "wrong # args: should be \"tkcon gets\""
|
2673 |
|
|
}
|
2674 |
|
|
set t $::tkcon::PRIV(base).gets
|
2675 |
|
|
if {![winfo exists $t]} {
|
2676 |
|
|
toplevel $t
|
2677 |
|
|
wm withdraw $t
|
2678 |
|
|
wm title $t "tkcon gets stdin request"
|
2679 |
|
|
label $t.gets -text "\"gets stdin\" request:"
|
2680 |
|
|
text $t.data -width 32 -height 5 -wrap none \
|
2681 |
|
|
-xscrollcommand [list $t.sx set] \
|
2682 |
|
|
-yscrollcommand [list $t.sy set]
|
2683 |
|
|
scrollbar $t.sx -orient h -takefocus 0 -highlightthick 0 \
|
2684 |
|
|
-command [list $t.data xview]
|
2685 |
|
|
scrollbar $t.sy -orient v -takefocus 0 -highlightthick 0 \
|
2686 |
|
|
-command [list $t.data yview]
|
2687 |
|
|
button $t.ok -text "OK" -command {set ::tkcon::PRIV(grab) 1}
|
2688 |
|
|
bind $t.ok <Return> { %W invoke }
|
2689 |
|
|
grid $t.gets - -sticky ew
|
2690 |
|
|
grid $t.data $t.sy -sticky news
|
2691 |
|
|
grid $t.sx -sticky ew
|
2692 |
|
|
grid $t.ok - -sticky ew
|
2693 |
|
|
grid columnconfig $t 0 -weight 1
|
2694 |
|
|
grid rowconfig $t 1 -weight 1
|
2695 |
|
|
wm transient $t $::tkcon::PRIV(root)
|
2696 |
|
|
wm geometry $t +[expr {([winfo screenwidth $t]-[winfo \
|
2697 |
|
|
reqwidth $t]) / 2}]+[expr {([winfo \
|
2698 |
|
|
screenheight $t]-[winfo reqheight $t]) / 2}]
|
2699 |
|
|
}
|
2700 |
|
|
$t.data delete 1.0 end
|
2701 |
|
|
wm deiconify $t
|
2702 |
|
|
raise $t
|
2703 |
|
|
grab $t
|
2704 |
|
|
focus $t.data
|
2705 |
|
|
vwait ::tkcon::PRIV(grab)
|
2706 |
|
|
grab release $t
|
2707 |
|
|
wm withdraw $t
|
2708 |
|
|
return [$t.data get 1.0 end-1c]
|
2709 |
|
|
}
|
2710 |
|
|
err* {
|
2711 |
|
|
## Outputs stack caused by last error.
|
2712 |
|
|
## error handling with pizazz (but with pizza would be nice too)
|
2713 |
|
|
if {[llength $args]==2} {
|
2714 |
|
|
set app [lindex $args 0]
|
2715 |
|
|
set type [lindex $args 1]
|
2716 |
|
|
if {[catch {::tkcon::EvalOther $app $type set errorInfo} info]} {
|
2717 |
|
|
set info "error getting info from $type $app:\n$info"
|
2718 |
|
|
}
|
2719 |
|
|
} else {
|
2720 |
|
|
set info $::tkcon::PRIV(errorInfo)
|
2721 |
|
|
}
|
2722 |
|
|
if {[string match {} $info]} { set info "errorInfo empty" }
|
2723 |
|
|
## If args is empty, the -attach switch just ignores it
|
2724 |
|
|
edit -attach $args -type error -- $info
|
2725 |
|
|
}
|
2726 |
|
|
fi* {
|
2727 |
|
|
## 'find' string
|
2728 |
|
|
::tkcon::Find $::tkcon::PRIV(console) $args
|
2729 |
|
|
}
|
2730 |
|
|
fo* {
|
2731 |
|
|
## 'font' ?fontname? - gets/sets the font of the console
|
2732 |
|
|
if {[llength $args]} {
|
2733 |
|
|
if {[info exists ::tkcon::PRIV(console)] && \
|
2734 |
|
|
[winfo exists $::tkcon::PRIV(console)]} {
|
2735 |
|
|
$::tkcon::PRIV(console) config -font $args
|
2736 |
|
|
set ::tkcon::OPT(font) [$::tkcon::PRIV(console) cget -font]
|
2737 |
|
|
} else {
|
2738 |
|
|
set ::tkcon::OPT(font) $args
|
2739 |
|
|
}
|
2740 |
|
|
}
|
2741 |
|
|
return $::tkcon::OPT(font)
|
2742 |
|
|
}
|
2743 |
|
|
hid* - with* {
|
2744 |
|
|
## 'hide' 'withdraw' - hides the console.
|
2745 |
|
|
wm withdraw $::tkcon::PRIV(root)
|
2746 |
|
|
}
|
2747 |
|
|
his* {
|
2748 |
|
|
## 'history'
|
2749 |
|
|
set sub {\2}
|
2750 |
|
|
if {[string match -new* $args]} { append sub "\n"}
|
2751 |
|
|
set h [::tkcon::EvalSlave history]
|
2752 |
|
|
regsub -all "( *\[0-9\]+ |\t)(\[^\n\]*\n?)" $h $sub h
|
2753 |
|
|
return $h
|
2754 |
|
|
}
|
2755 |
|
|
ico* {
|
2756 |
|
|
## 'iconify' - iconifies the console with 'iconify'.
|
2757 |
|
|
wm iconify $::tkcon::PRIV(root)
|
2758 |
|
|
}
|
2759 |
|
|
mas* - eval {
|
2760 |
|
|
## 'master' - evals contents in master interpreter
|
2761 |
|
|
uplevel \#0 $args
|
2762 |
|
|
}
|
2763 |
|
|
set {
|
2764 |
|
|
## 'set' - set (or get, or unset) simple vars (not whole arrays)
|
2765 |
|
|
## from the master console interpreter
|
2766 |
|
|
## possible formats:
|
2767 |
|
|
## tkcon set <var>
|
2768 |
|
|
## tkcon set <var> <value>
|
2769 |
|
|
## tkcon set <var> <interp> <var1> <var2> w
|
2770 |
|
|
## tkcon set <var> <interp> <var1> <var2> u
|
2771 |
|
|
## tkcon set <var> <interp> <var1> <var2> r
|
2772 |
|
|
if {[llength $args]==5} {
|
2773 |
|
|
## This is for use w/ 'tkcon upvar' and only works with slaves
|
2774 |
|
|
foreach {var i var1 var2 op} $args break
|
2775 |
|
|
if {[string compare {} $var2]} { append var1 "($var2)" }
|
2776 |
|
|
switch $op {
|
2777 |
|
|
u { uplevel \#0 [list unset $var] }
|
2778 |
|
|
w {
|
2779 |
|
|
return [uplevel \#0 [list set $var \
|
2780 |
|
|
[interp eval $i [list set $var1]]]]
|
2781 |
|
|
}
|
2782 |
|
|
r {
|
2783 |
|
|
return [interp eval $i [list set $var1 \
|
2784 |
|
|
[uplevel \#0 [list set $var]]]]
|
2785 |
|
|
}
|
2786 |
|
|
}
|
2787 |
|
|
} elseif {[llength $args] == 1} {
|
2788 |
|
|
upvar \#0 [lindex $args 0] var
|
2789 |
|
|
if {[array exists var]} {
|
2790 |
|
|
return [array get var]
|
2791 |
|
|
} else {
|
2792 |
|
|
return $var
|
2793 |
|
|
}
|
2794 |
|
|
}
|
2795 |
|
|
return [uplevel \#0 set $args]
|
2796 |
|
|
}
|
2797 |
|
|
append {
|
2798 |
|
|
## Modify a var in the master environment using append
|
2799 |
|
|
return [uplevel \#0 append $args]
|
2800 |
|
|
}
|
2801 |
|
|
lappend {
|
2802 |
|
|
## Modify a var in the master environment using lappend
|
2803 |
|
|
return [uplevel \#0 lappend $args]
|
2804 |
|
|
}
|
2805 |
|
|
sh* - dei* {
|
2806 |
|
|
## 'show|deiconify' - deiconifies the console.
|
2807 |
|
|
wm deiconify $::tkcon::PRIV(root)
|
2808 |
|
|
raise $::tkcon::PRIV(root)
|
2809 |
|
|
focus -force $::tkcon::PRIV(console)
|
2810 |
|
|
}
|
2811 |
|
|
ti* {
|
2812 |
|
|
## 'title' ?title? - gets/sets the console's title
|
2813 |
|
|
if {[llength $args]} {
|
2814 |
|
|
return [wm title $::tkcon::PRIV(root) [join $args]]
|
2815 |
|
|
} else {
|
2816 |
|
|
return [wm title $::tkcon::PRIV(root)]
|
2817 |
|
|
}
|
2818 |
|
|
}
|
2819 |
|
|
upv* {
|
2820 |
|
|
## 'upvar' masterVar slaveVar
|
2821 |
|
|
## link slave variable slaveVar to the master variable masterVar
|
2822 |
|
|
## only works masters<->slave
|
2823 |
|
|
set masterVar [lindex $args 0]
|
2824 |
|
|
set slaveVar [lindex $args 1]
|
2825 |
|
|
if {[info exists $masterVar]} {
|
2826 |
|
|
interp eval $::tkcon::OPT(exec) \
|
2827 |
|
|
[list set $slaveVar [set $masterVar]]
|
2828 |
|
|
} else {
|
2829 |
|
|
catch {interp eval $::tkcon::OPT(exec) [list unset $slaveVar]}
|
2830 |
|
|
}
|
2831 |
|
|
interp eval $::tkcon::OPT(exec) \
|
2832 |
|
|
[list trace variable $slaveVar rwu \
|
2833 |
|
|
[list tkcon set $masterVar $::tkcon::OPT(exec)]]
|
2834 |
|
|
return
|
2835 |
|
|
}
|
2836 |
|
|
v* {
|
2837 |
|
|
return $::tkcon::PRIV(version)
|
2838 |
|
|
}
|
2839 |
|
|
default {
|
2840 |
|
|
## tries to determine if the command exists, otherwise throws error
|
2841 |
|
|
set new ::tkcon::[string toupper \
|
2842 |
|
|
[string index $cmd 0]][string range $cmd 1 end]
|
2843 |
|
|
if {[llength [info command $new]]} {
|
2844 |
|
|
uplevel \#0 $new $args
|
2845 |
|
|
} else {
|
2846 |
|
|
return -code error "bad option \"$cmd\": must be\
|
2847 |
|
|
[join [lsort [list attach close console destroy \
|
2848 |
|
|
font hide iconify load main master new save show \
|
2849 |
|
|
slave deiconify version title bgerror]] {, }]"
|
2850 |
|
|
}
|
2851 |
|
|
}
|
2852 |
|
|
}
|
2853 |
|
|
}
|
2854 |
|
|
|
2855 |
|
|
##
|
2856 |
|
|
## Some procedures to make up for lack of built-in shell commands
|
2857 |
|
|
##
|
2858 |
|
|
|
2859 |
|
|
## tkcon_puts -
|
2860 |
|
|
## This allows me to capture all stdout/stderr to the console window
|
2861 |
|
|
## This will be renamed to 'puts' at the appropriate time during init
|
2862 |
|
|
##
|
2863 |
|
|
# ARGS: same as usual
|
2864 |
|
|
# Outputs: the string with a color-coded text tag
|
2865 |
|
|
##
|
2866 |
|
|
proc tkcon_puts args {
|
2867 |
|
|
set len [llength $args]
|
2868 |
|
|
foreach {arg1 arg2 arg3} $args { break }
|
2869 |
|
|
|
2870 |
|
|
if {$len == 1} {
|
2871 |
|
|
tkcon console insert output "$arg1\n" stdout
|
2872 |
|
|
} elseif {$len == 2} {
|
2873 |
|
|
if {![string compare $arg1 -nonewline]} {
|
2874 |
|
|
tkcon console insert output $arg2 stdout
|
2875 |
|
|
} elseif {![string compare $arg1 stdout] \
|
2876 |
|
|
|| ![string compare $arg1 stderr]} {
|
2877 |
|
|
tkcon console insert output "$arg2\n" $arg1
|
2878 |
|
|
} else {
|
2879 |
|
|
set len 0
|
2880 |
|
|
}
|
2881 |
|
|
} elseif {$len == 3} {
|
2882 |
|
|
if {![string compare $arg1 -nonewline] \
|
2883 |
|
|
&& (![string compare $arg2 stdout] \
|
2884 |
|
|
|| ![string compare $arg2 stderr])} {
|
2885 |
|
|
tkcon console insert output $arg3 $arg2
|
2886 |
|
|
} elseif {(![string compare $arg1 stdout] \
|
2887 |
|
|
|| ![string compare $arg1 stderr]) \
|
2888 |
|
|
&& ![string compare $arg3 nonewline]} {
|
2889 |
|
|
tkcon console insert output $arg2 $arg1
|
2890 |
|
|
} else {
|
2891 |
|
|
set len 0
|
2892 |
|
|
}
|
2893 |
|
|
} else {
|
2894 |
|
|
set len 0
|
2895 |
|
|
}
|
2896 |
|
|
|
2897 |
|
|
## $len == 0 means it wasn't handled by tkcon above.
|
2898 |
|
|
##
|
2899 |
|
|
if {$len == 0} {
|
2900 |
|
|
global errorCode errorInfo
|
2901 |
|
|
if {[catch "tkcon_tcl_puts $args" msg]} {
|
2902 |
|
|
regsub tkcon_tcl_puts $msg puts msg
|
2903 |
|
|
regsub -all tkcon_tcl_puts $errorInfo puts errorInfo
|
2904 |
|
|
return -code error $msg
|
2905 |
|
|
}
|
2906 |
|
|
return $msg
|
2907 |
|
|
}
|
2908 |
|
|
|
2909 |
|
|
## WARNING: This update should behave well because it uses idletasks,
|
2910 |
|
|
## however, if there are weird looping problems with events, or
|
2911 |
|
|
## hanging in waits, try commenting this out.
|
2912 |
|
|
if {$len} {
|
2913 |
|
|
tkcon console see output
|
2914 |
|
|
update idletasks
|
2915 |
|
|
}
|
2916 |
|
|
}
|
2917 |
|
|
|
2918 |
|
|
## tkcon_gets -
|
2919 |
|
|
## This allows me to capture all stdin input without needing to stdin
|
2920 |
|
|
## This will be renamed to 'gets' at the appropriate time during init
|
2921 |
|
|
##
|
2922 |
|
|
# ARGS: same as gets
|
2923 |
|
|
# Outputs: same as gets
|
2924 |
|
|
##
|
2925 |
|
|
proc tkcon_gets args {
|
2926 |
|
|
set len [llength $args]
|
2927 |
|
|
if {$len != 1 && $len != 2} {
|
2928 |
|
|
return -code error \
|
2929 |
|
|
"wrong # args: should be \"gets channelId ?varName?\""
|
2930 |
|
|
}
|
2931 |
|
|
if {[string compare stdin [lindex $args 0]]} {
|
2932 |
|
|
return [uplevel 1 tkcon_tcl_gets $args]
|
2933 |
|
|
}
|
2934 |
|
|
set gtype [tkcon set ::tkcon::OPT(gets)]
|
2935 |
|
|
if {$gtype == ""} { set gtype congets }
|
2936 |
|
|
set data [tkcon $gtype]
|
2937 |
|
|
if {$len == 2} {
|
2938 |
|
|
upvar 1 [lindex $args 1] var
|
2939 |
|
|
set var $data
|
2940 |
|
|
return [string length $data]
|
2941 |
|
|
}
|
2942 |
|
|
return $data
|
2943 |
|
|
}
|
2944 |
|
|
|
2945 |
|
|
## edit - opens a file/proc/var for reading/editing
|
2946 |
|
|
##
|
2947 |
|
|
# Arguments:
|
2948 |
|
|
# type proc/file/var
|
2949 |
|
|
# what the actual name of the item
|
2950 |
|
|
# Returns: nothing
|
2951 |
|
|
##
|
2952 |
|
|
proc edit {args} {
|
2953 |
|
|
array set opts {-find {} -type {} -attach {}}
|
2954 |
|
|
while {[string match -* [lindex $args 0]]} {
|
2955 |
|
|
switch -glob -- [lindex $args 0] {
|
2956 |
|
|
-f* { set opts(-find) [lindex $args 1] }
|
2957 |
|
|
-a* { set opts(-attach) [lindex $args 1] }
|
2958 |
|
|
-t* { set opts(-type) [lindex $args 1] }
|
2959 |
|
|
-- { set args [lreplace $args 0 0]; break }
|
2960 |
|
|
default {return -code error "unknown option \"[lindex $args 0]\""}
|
2961 |
|
|
}
|
2962 |
|
|
set args [lreplace $args 0 1]
|
2963 |
|
|
}
|
2964 |
|
|
# determine who we are dealing with
|
2965 |
|
|
if {[llength $opts(-attach)]} {
|
2966 |
|
|
foreach {app type} $opts(-attach) {break}
|
2967 |
|
|
} else {
|
2968 |
|
|
foreach {app type} [tkcon attach] {break}
|
2969 |
|
|
}
|
2970 |
|
|
|
2971 |
|
|
set word [lindex $args 0]
|
2972 |
|
|
if {[string match {} $opts(-type)]} {
|
2973 |
|
|
if {[llength [::tkcon::EvalOther $app $type info commands [list $word]]]} {
|
2974 |
|
|
set opts(-type) "proc"
|
2975 |
|
|
} elseif {[llength [::tkcon::EvalOther $app $type info vars [list $word]]]} {
|
2976 |
|
|
set opts(-type) "var"
|
2977 |
|
|
} elseif {[::tkcon::EvalOther $app $type file isfile [list $word]]} {
|
2978 |
|
|
set opts(-type) "file"
|
2979 |
|
|
}
|
2980 |
|
|
}
|
2981 |
|
|
if {[string compare $opts(-type) {}]} {
|
2982 |
|
|
# Create unique edit window toplevel
|
2983 |
|
|
set w $::tkcon::PRIV(base).__edit
|
2984 |
|
|
set i 0
|
2985 |
|
|
while {[winfo exists $w[incr i]]} {}
|
2986 |
|
|
append w $i
|
2987 |
|
|
toplevel $w
|
2988 |
|
|
wm withdraw $w
|
2989 |
|
|
if {[string length $word] > 12} {
|
2990 |
|
|
wm title $w "tkcon Edit: [string range $word 0 9]..."
|
2991 |
|
|
} else {
|
2992 |
|
|
wm title $w "tkcon Edit: $word"
|
2993 |
|
|
}
|
2994 |
|
|
|
2995 |
|
|
text $w.text -wrap none \
|
2996 |
|
|
-xscrollcommand [list $w.sx set] \
|
2997 |
|
|
-yscrollcommand [list $w.sy set] \
|
2998 |
|
|
-foreground $::tkcon::COLOR(stdin) \
|
2999 |
|
|
-background $::tkcon::COLOR(bg) \
|
3000 |
|
|
-insertbackground $::tkcon::COLOR(cursor) \
|
3001 |
|
|
-font $::tkcon::OPT(font)
|
3002 |
|
|
scrollbar $w.sx -orient h -takefocus 0 -bd 1 \
|
3003 |
|
|
-command [list $w.text xview]
|
3004 |
|
|
scrollbar $w.sy -orient v -takefocus 0 -bd 1 \
|
3005 |
|
|
-command [list $w.text yview]
|
3006 |
|
|
|
3007 |
|
|
set menu [menu $w.mbar]
|
3008 |
|
|
$w configure -menu $menu
|
3009 |
|
|
|
3010 |
|
|
## File Menu
|
3011 |
|
|
##
|
3012 |
|
|
set m [menu [::tkcon::MenuButton $menu File file]]
|
3013 |
|
|
$m add command -label "Save As..." -underline 0 \
|
3014 |
|
|
-command [list ::tkcon::Save {} widget $w.text]
|
3015 |
|
|
$m add command -label "Append To..." -underline 0 \
|
3016 |
|
|
-command [list ::tkcon::Save {} widget $w.text a+]
|
3017 |
|
|
$m add separator
|
3018 |
|
|
$m add command -label "Dismiss" -underline 0 -accel "Ctrl-w" \
|
3019 |
|
|
-command [list destroy $w]
|
3020 |
|
|
bind $w <Control-w> [list destroy $w]
|
3021 |
|
|
bind $w <$::tkcon::PRIV(meta)-w> [list destroy $w]
|
3022 |
|
|
|
3023 |
|
|
## Edit Menu
|
3024 |
|
|
##
|
3025 |
|
|
set text $w.text
|
3026 |
|
|
set m [menu [::tkcon::MenuButton $menu Edit edit]]
|
3027 |
|
|
$m add command -label "Cut" -under 2 \
|
3028 |
|
|
-command [list tk_textCut $text]
|
3029 |
|
|
$m add command -label "Copy" -under 0 \
|
3030 |
|
|
-command [list tk_textCopy $text]
|
3031 |
|
|
$m add command -label "Paste" -under 0 \
|
3032 |
|
|
-command [list tk_textPaste $text]
|
3033 |
|
|
$m add separator
|
3034 |
|
|
$m add command -label "Find" -under 0 \
|
3035 |
|
|
-command [list ::tkcon::FindBox $text]
|
3036 |
|
|
|
3037 |
|
|
## Send To Menu
|
3038 |
|
|
##
|
3039 |
|
|
set m [menu [::tkcon::MenuButton $menu "Send To..." send]]
|
3040 |
|
|
$m add command -label "Send To $app" -underline 0 \
|
3041 |
|
|
-command "::tkcon::EvalOther [list $app] $type \
|
3042 |
|
|
eval \[$w.text get 1.0 end-1c\]"
|
3043 |
|
|
set other [tkcon attach]
|
3044 |
|
|
if {[string compare $other [list $app $type]]} {
|
3045 |
|
|
$m add command -label "Send To [lindex $other 0]" \
|
3046 |
|
|
-command "::tkcon::EvalOther $other \
|
3047 |
|
|
eval \[$w.text get 1.0 end-1c\]"
|
3048 |
|
|
}
|
3049 |
|
|
|
3050 |
|
|
grid $w.text - $w.sy -sticky news
|
3051 |
|
|
grid $w.sx - -sticky ew
|
3052 |
|
|
grid columnconfigure $w 0 -weight 1
|
3053 |
|
|
grid columnconfigure $w 1 -weight 1
|
3054 |
|
|
grid rowconfigure $w 0 -weight 1
|
3055 |
|
|
} else {
|
3056 |
|
|
return -code error "unrecognized type '$word'"
|
3057 |
|
|
}
|
3058 |
|
|
switch -glob -- $opts(-type) {
|
3059 |
|
|
proc* {
|
3060 |
|
|
$w.text insert 1.0 \
|
3061 |
|
|
[::tkcon::EvalOther $app $type dump proc [list $word]]
|
3062 |
|
|
}
|
3063 |
|
|
var* {
|
3064 |
|
|
$w.text insert 1.0 \
|
3065 |
|
|
[::tkcon::EvalOther $app $type dump var [list $word]]
|
3066 |
|
|
}
|
3067 |
|
|
file {
|
3068 |
|
|
$w.text insert 1.0 [::tkcon::EvalOther $app $type eval \
|
3069 |
|
|
[subst -nocommands {
|
3070 |
|
|
set __tkcon(fid) [open $word r]
|
3071 |
|
|
set __tkcon(data) [read \$__tkcon(fid)]
|
3072 |
|
|
close \$__tkcon(fid)
|
3073 |
|
|
after 1000 unset __tkcon
|
3074 |
|
|
return \$__tkcon(data)
|
3075 |
|
|
}
|
3076 |
|
|
]]
|
3077 |
|
|
}
|
3078 |
|
|
error* {
|
3079 |
|
|
$w.text insert 1.0 [join $args \n]
|
3080 |
|
|
::tkcon::ErrorHighlight $w.text
|
3081 |
|
|
}
|
3082 |
|
|
default {
|
3083 |
|
|
$w.text insert 1.0 [join $args \n]
|
3084 |
|
|
}
|
3085 |
|
|
}
|
3086 |
|
|
wm deiconify $w
|
3087 |
|
|
focus $w.text
|
3088 |
|
|
if {[string compare $opts(-find) {}]} {
|
3089 |
|
|
::tkcon::Find $w.text $opts(-find) -case 1
|
3090 |
|
|
}
|
3091 |
|
|
}
|
3092 |
|
|
interp alias {} ::more {} ::edit
|
3093 |
|
|
interp alias {} ::less {} ::edit
|
3094 |
|
|
|
3095 |
|
|
## echo
|
3096 |
|
|
## Relaxes the one string restriction of 'puts'
|
3097 |
|
|
# ARGS: any number of strings to output to stdout
|
3098 |
|
|
##
|
3099 |
|
|
proc echo args { puts [concat $args] }
|
3100 |
|
|
|
3101 |
|
|
## clear - clears the buffer of the console (not the history though)
|
3102 |
|
|
## This is executed in the parent interpreter
|
3103 |
|
|
##
|
3104 |
|
|
proc clear {{pcnt 100}} {
|
3105 |
|
|
if {![regexp {^[0-9]*$} $pcnt] || $pcnt < 1 || $pcnt > 100} {
|
3106 |
|
|
return -code error \
|
3107 |
|
|
"invalid percentage to clear: must be 1-100 (100 default)"
|
3108 |
|
|
} elseif {$pcnt == 100} {
|
3109 |
|
|
tkcon console delete 1.0 end
|
3110 |
|
|
} else {
|
3111 |
|
|
set tmp [expr {$pcnt/100.0*[tkcon console index end]}]
|
3112 |
|
|
tkcon console delete 1.0 "$tmp linestart"
|
3113 |
|
|
}
|
3114 |
|
|
}
|
3115 |
|
|
|
3116 |
|
|
## alias - akin to the csh alias command
|
3117 |
|
|
## If called with no args, then it dumps out all current aliases
|
3118 |
|
|
## If called with one arg, returns the alias of that arg (or {} if none)
|
3119 |
|
|
# ARGS: newcmd - (optional) command to bind alias to
|
3120 |
|
|
# args - command and args being aliased
|
3121 |
|
|
##
|
3122 |
|
|
proc alias {{newcmd {}} args} {
|
3123 |
|
|
if {[string match {} $newcmd]} {
|
3124 |
|
|
set res {}
|
3125 |
|
|
foreach a [interp aliases] {
|
3126 |
|
|
lappend res [list $a -> [interp alias {} $a]]
|
3127 |
|
|
}
|
3128 |
|
|
return [join $res \n]
|
3129 |
|
|
} elseif {![llength $args]} {
|
3130 |
|
|
interp alias {} $newcmd
|
3131 |
|
|
} else {
|
3132 |
|
|
eval interp alias [list {} $newcmd {}] $args
|
3133 |
|
|
}
|
3134 |
|
|
}
|
3135 |
|
|
|
3136 |
|
|
## unalias - unaliases an alias'ed command
|
3137 |
|
|
# ARGS: cmd - command to unbind as an alias
|
3138 |
|
|
##
|
3139 |
|
|
proc unalias {cmd} {
|
3140 |
|
|
interp alias {} $cmd {}
|
3141 |
|
|
}
|
3142 |
|
|
|
3143 |
|
|
## dump - outputs variables/procedure/widget info in source'able form.
|
3144 |
|
|
## Accepts glob style pattern matching for the names
|
3145 |
|
|
#
|
3146 |
|
|
# ARGS: type - type of thing to dump: must be variable, procedure, widget
|
3147 |
|
|
#
|
3148 |
|
|
# OPTS: -nocomplain
|
3149 |
|
|
# don't complain if no items of the specified type are found
|
3150 |
|
|
# -filter pattern
|
3151 |
|
|
# specifies a glob filter pattern to be used by the variable
|
3152 |
|
|
# method as an array filter pattern (it filters down for
|
3153 |
|
|
# nested elements) and in the widget method as a config
|
3154 |
|
|
# option filter pattern
|
3155 |
|
|
# -- forcibly ends options recognition
|
3156 |
|
|
#
|
3157 |
|
|
# Returns: the values of the requested items in a 'source'able form
|
3158 |
|
|
##
|
3159 |
|
|
proc dump {type args} {
|
3160 |
|
|
set whine 1
|
3161 |
|
|
set code ok
|
3162 |
|
|
if {![llength $args]} {
|
3163 |
|
|
## If no args, assume they gave us something to dump and
|
3164 |
|
|
## we'll try anything
|
3165 |
|
|
set args $type
|
3166 |
|
|
set type any
|
3167 |
|
|
}
|
3168 |
|
|
while {[string match -* [lindex $args 0]]} {
|
3169 |
|
|
switch -glob -- [lindex $args 0] {
|
3170 |
|
|
-n* { set whine 0; set args [lreplace $args 0 0] }
|
3171 |
|
|
-f* { set fltr [lindex $args 1]; set args [lreplace $args 0 1] }
|
3172 |
|
|
-- { set args [lreplace $args 0 0]; break }
|
3173 |
|
|
default {return -code error "unknown option \"[lindex $args 0]\""}
|
3174 |
|
|
}
|
3175 |
|
|
}
|
3176 |
|
|
if {$whine && ![llength $args]} {
|
3177 |
|
|
return -code error "wrong \# args: [lindex [info level 0] 0] type\
|
3178 |
|
|
?-nocomplain? ?-filter pattern? ?--? pattern ?pattern ...?"
|
3179 |
|
|
}
|
3180 |
|
|
set res {}
|
3181 |
|
|
switch -glob -- $type {
|
3182 |
|
|
c* {
|
3183 |
|
|
# command
|
3184 |
|
|
# outputs commands by figuring out, as well as possible, what it is
|
3185 |
|
|
# this does not attempt to auto-load anything
|
3186 |
|
|
foreach arg $args {
|
3187 |
|
|
if {[llength [set cmds [info commands $arg]]]} {
|
3188 |
|
|
foreach cmd [lsort $cmds] {
|
3189 |
|
|
if {[lsearch -exact [interp aliases] $cmd] > -1} {
|
3190 |
|
|
append res "\#\# ALIAS: $cmd =>\
|
3191 |
|
|
[interp alias {} $cmd]\n"
|
3192 |
|
|
} elseif {
|
3193 |
|
|
[llength [info procs $cmd]] ||
|
3194 |
|
|
([string match *::* $cmd] &&
|
3195 |
|
|
[llength [namespace eval [namespace qual $cmd] \
|
3196 |
|
|
info procs [namespace tail $cmd]]])
|
3197 |
|
|
} {
|
3198 |
|
|
if {[catch {dump p -- $cmd} msg] && $whine} {
|
3199 |
|
|
set code error
|
3200 |
|
|
}
|
3201 |
|
|
append res $msg\n
|
3202 |
|
|
} else {
|
3203 |
|
|
append res "\#\# COMMAND: $cmd\n"
|
3204 |
|
|
}
|
3205 |
|
|
}
|
3206 |
|
|
} elseif {$whine} {
|
3207 |
|
|
append res "\#\# No known command $arg\n"
|
3208 |
|
|
set code error
|
3209 |
|
|
}
|
3210 |
|
|
}
|
3211 |
|
|
}
|
3212 |
|
|
v* {
|
3213 |
|
|
# variable
|
3214 |
|
|
# outputs variables value(s), whether array or simple.
|
3215 |
|
|
if {![info exists fltr]} { set fltr * }
|
3216 |
|
|
foreach arg $args {
|
3217 |
|
|
if {![llength [set vars [uplevel 1 info vars [list $arg]]]]} {
|
3218 |
|
|
if {[uplevel 1 info exists $arg]} {
|
3219 |
|
|
set vars $arg
|
3220 |
|
|
} elseif {$whine} {
|
3221 |
|
|
append res "\#\# No known variable $arg\n"
|
3222 |
|
|
set code error
|
3223 |
|
|
continue
|
3224 |
|
|
} else { continue }
|
3225 |
|
|
}
|
3226 |
|
|
foreach var [lsort $vars] {
|
3227 |
|
|
if {[uplevel 1 [list info locals $var]] == ""} {
|
3228 |
|
|
# use the proper scope of the var, but
|
3229 |
|
|
# namespace which won't id locals correctly
|
3230 |
|
|
set var [uplevel 1 \
|
3231 |
|
|
[list namespace which -variable $var]]
|
3232 |
|
|
}
|
3233 |
|
|
upvar 1 $var v
|
3234 |
|
|
if {[array exists v] || [catch {string length $v}]} {
|
3235 |
|
|
set nst {}
|
3236 |
|
|
append res "array set [list $var] \{\n"
|
3237 |
|
|
if {[array size v]} {
|
3238 |
|
|
foreach i [lsort [array names v $fltr]] {
|
3239 |
|
|
upvar 0 v\($i\) __a
|
3240 |
|
|
if {[array exists __a]} {
|
3241 |
|
|
append nst "\#\# NESTED ARRAY ELEM: $i\n"
|
3242 |
|
|
append nst "upvar 0 [list $var\($i\)] __a;\
|
3243 |
|
|
[dump v -filter $fltr __a]\n"
|
3244 |
|
|
} else {
|
3245 |
|
|
append res " [list $i]\t[list $v($i)]\n"
|
3246 |
|
|
}
|
3247 |
|
|
}
|
3248 |
|
|
} else {
|
3249 |
|
|
## empty array
|
3250 |
|
|
append res " empty array\n"
|
3251 |
|
|
append nst "unset [list $var](empty)\n"
|
3252 |
|
|
}
|
3253 |
|
|
append res "\}\n$nst"
|
3254 |
|
|
} else {
|
3255 |
|
|
append res [list set $var $v]\n
|
3256 |
|
|
}
|
3257 |
|
|
}
|
3258 |
|
|
}
|
3259 |
|
|
}
|
3260 |
|
|
p* {
|
3261 |
|
|
# procedure
|
3262 |
|
|
foreach arg $args {
|
3263 |
|
|
if {
|
3264 |
|
|
![llength [set procs [info proc $arg]]] &&
|
3265 |
|
|
([string match *::* $arg] &&
|
3266 |
|
|
[llength [set ps [namespace eval \
|
3267 |
|
|
[namespace qualifier $arg] \
|
3268 |
|
|
info procs [namespace tail $arg]]]])
|
3269 |
|
|
} {
|
3270 |
|
|
set procs {}
|
3271 |
|
|
set namesp [namespace qualifier $arg]
|
3272 |
|
|
foreach p $ps {
|
3273 |
|
|
lappend procs ${namesp}::$p
|
3274 |
|
|
}
|
3275 |
|
|
}
|
3276 |
|
|
if {[llength $procs]} {
|
3277 |
|
|
foreach p [lsort $procs] {
|
3278 |
|
|
set as {}
|
3279 |
|
|
foreach a [info args $p] {
|
3280 |
|
|
if {[info default $p $a tmp]} {
|
3281 |
|
|
lappend as [list $a $tmp]
|
3282 |
|
|
} else {
|
3283 |
|
|
lappend as $a
|
3284 |
|
|
}
|
3285 |
|
|
}
|
3286 |
|
|
append res [list proc $p $as [info body $p]]\n
|
3287 |
|
|
}
|
3288 |
|
|
} elseif {$whine} {
|
3289 |
|
|
append res "\#\# No known proc $arg\n"
|
3290 |
|
|
set code error
|
3291 |
|
|
}
|
3292 |
|
|
}
|
3293 |
|
|
}
|
3294 |
|
|
w* {
|
3295 |
|
|
# widget
|
3296 |
|
|
## The user should have Tk loaded
|
3297 |
|
|
if {![llength [info command winfo]]} {
|
3298 |
|
|
return -code error "winfo not present, cannot dump widgets"
|
3299 |
|
|
}
|
3300 |
|
|
if {![info exists fltr]} { set fltr .* }
|
3301 |
|
|
foreach arg $args {
|
3302 |
|
|
if {[llength [set ws [info command $arg]]]} {
|
3303 |
|
|
foreach w [lsort $ws] {
|
3304 |
|
|
if {[winfo exists $w]} {
|
3305 |
|
|
if {[catch {$w configure} cfg]} {
|
3306 |
|
|
append res "\#\# Widget $w\
|
3307 |
|
|
does not support configure method"
|
3308 |
|
|
set code error
|
3309 |
|
|
} else {
|
3310 |
|
|
append res "\#\# [winfo class $w]\
|
3311 |
|
|
$w\n$w configure"
|
3312 |
|
|
foreach c $cfg {
|
3313 |
|
|
if {[llength $c] != 5} continue
|
3314 |
|
|
## Check to see that the option does
|
3315 |
|
|
## not match the default, then check
|
3316 |
|
|
## the item against the user filter
|
3317 |
|
|
if {[string compare [lindex $c 3] \
|
3318 |
|
|
[lindex $c 4]] && \
|
3319 |
|
|
[regexp -nocase -- $fltr $c]} {
|
3320 |
|
|
append res " \\\n\t[list [lindex $c 0]\
|
3321 |
|
|
[lindex $c 4]]"
|
3322 |
|
|
}
|
3323 |
|
|
}
|
3324 |
|
|
append res \n
|
3325 |
|
|
}
|
3326 |
|
|
}
|
3327 |
|
|
}
|
3328 |
|
|
} elseif {$whine} {
|
3329 |
|
|
append res "\#\# No known widget $arg\n"
|
3330 |
|
|
set code error
|
3331 |
|
|
}
|
3332 |
|
|
}
|
3333 |
|
|
}
|
3334 |
|
|
a* {
|
3335 |
|
|
## see if we recognize it, other complain
|
3336 |
|
|
if {[regexp {(var|com|proc|widget)} \
|
3337 |
|
|
[set types [uplevel 1 what $args]]]} {
|
3338 |
|
|
foreach type $types {
|
3339 |
|
|
if {[regexp {(var|com|proc|widget)} $type]} {
|
3340 |
|
|
append res "[uplevel 1 dump $type $args]\n"
|
3341 |
|
|
}
|
3342 |
|
|
}
|
3343 |
|
|
} else {
|
3344 |
|
|
set res "dump was unable to resolve type for \"$args\""
|
3345 |
|
|
set code error
|
3346 |
|
|
}
|
3347 |
|
|
}
|
3348 |
|
|
default {
|
3349 |
|
|
return -code error "bad [lindex [info level 0] 0] option\
|
3350 |
|
|
\"$type\": must be variable, command, procedure,\
|
3351 |
|
|
or widget"
|
3352 |
|
|
}
|
3353 |
|
|
}
|
3354 |
|
|
return -code $code [string trimright $res \n]
|
3355 |
|
|
}
|
3356 |
|
|
|
3357 |
|
|
## idebug - interactive debugger
|
3358 |
|
|
#
|
3359 |
|
|
# idebug body ?level?
|
3360 |
|
|
#
|
3361 |
|
|
# Prints out the body of the command (if it is a procedure) at the
|
3362 |
|
|
# specified level. <i>level</i> defaults to the current level.
|
3363 |
|
|
#
|
3364 |
|
|
# idebug break
|
3365 |
|
|
#
|
3366 |
|
|
# Creates a breakpoint within a procedure. This will only trigger
|
3367 |
|
|
# if idebug is on and the id matches the pattern. If so, TkCon will
|
3368 |
|
|
# pop to the front with the prompt changed to an idebug prompt. You
|
3369 |
|
|
# are given the basic ability to observe the call stack an query/set
|
3370 |
|
|
# variables or execute Tcl commands at any level. A separate history
|
3371 |
|
|
# is maintained in debugging mode.
|
3372 |
|
|
#
|
3373 |
|
|
# idebug echo|{echo ?id?} ?args?
|
3374 |
|
|
#
|
3375 |
|
|
# Behaves just like "echo", but only triggers when idebug is on.
|
3376 |
|
|
# You can specify an optional id to further restrict triggering.
|
3377 |
|
|
# If no id is specified, it defaults to the name of the command
|
3378 |
|
|
# in which the call was made.
|
3379 |
|
|
#
|
3380 |
|
|
# idebug id ?id?
|
3381 |
|
|
#
|
3382 |
|
|
# Query or set the idebug id. This id is used by other idebug
|
3383 |
|
|
# methods to determine if they should trigger or not. The idebug
|
3384 |
|
|
# id can be a glob pattern and defaults to *.
|
3385 |
|
|
#
|
3386 |
|
|
# idebug off
|
3387 |
|
|
#
|
3388 |
|
|
# Turns idebug off.
|
3389 |
|
|
#
|
3390 |
|
|
# idebug on ?id?
|
3391 |
|
|
#
|
3392 |
|
|
# Turns idebug on. If 'id' is specified, it sets the id to it.
|
3393 |
|
|
#
|
3394 |
|
|
# idebug puts|{puts ?id?} args
|
3395 |
|
|
#
|
3396 |
|
|
# Behaves just like "puts", but only triggers when idebug is on.
|
3397 |
|
|
# You can specify an optional id to further restrict triggering.
|
3398 |
|
|
# If no id is specified, it defaults to the name of the command
|
3399 |
|
|
# in which the call was made.
|
3400 |
|
|
#
|
3401 |
|
|
# idebug show type ?level? ?VERBOSE?
|
3402 |
|
|
#
|
3403 |
|
|
# 'type' must be one of vars, locals or globals. This method
|
3404 |
|
|
# will output the variables/locals/globals present in a particular
|
3405 |
|
|
# level. If VERBOSE is added, then it actually 'dump's out the
|
3406 |
|
|
# values as well. 'level' defaults to the level in which this
|
3407 |
|
|
# method was called.
|
3408 |
|
|
#
|
3409 |
|
|
# idebug trace ?level?
|
3410 |
|
|
#
|
3411 |
|
|
# Prints out the stack trace from the specified level up to the top
|
3412 |
|
|
# level. 'level' defaults to the current level.
|
3413 |
|
|
#
|
3414 |
|
|
##
|
3415 |
|
|
proc idebug {opt args} {
|
3416 |
|
|
global IDEBUG
|
3417 |
|
|
|
3418 |
|
|
if {![info exists IDEBUG(on)]} {
|
3419 |
|
|
array set IDEBUG { on 0 id * debugging 0 }
|
3420 |
|
|
}
|
3421 |
|
|
set level [expr {[info level]-1}]
|
3422 |
|
|
switch -glob -- $opt {
|
3423 |
|
|
on {
|
3424 |
|
|
if {[llength $args]} { set IDEBUG(id) $args }
|
3425 |
|
|
return [set IDEBUG(on) 1]
|
3426 |
|
|
}
|
3427 |
|
|
off { return [set IDEBUG(on) 0] }
|
3428 |
|
|
id {
|
3429 |
|
|
if {![llength $args]} {
|
3430 |
|
|
return $IDEBUG(id)
|
3431 |
|
|
} else { return [set IDEBUG(id) $args] }
|
3432 |
|
|
}
|
3433 |
|
|
break {
|
3434 |
|
|
if {!$IDEBUG(on) || $IDEBUG(debugging) || \
|
3435 |
|
|
([llength $args] && \
|
3436 |
|
|
![string match $IDEBUG(id) $args]) || [info level]<1} {
|
3437 |
|
|
return
|
3438 |
|
|
}
|
3439 |
|
|
set IDEBUG(debugging) 1
|
3440 |
|
|
puts stderr "idebug at level \#$level: [lindex [info level -1] 0]"
|
3441 |
|
|
set tkcon [llength [info command tkcon]]
|
3442 |
|
|
if {$tkcon} {
|
3443 |
|
|
tkcon master eval set ::tkcon::OPT(prompt2) \$::tkcon::OPT(prompt1)
|
3444 |
|
|
tkcon master eval set ::tkcon::OPT(prompt1) \$::tkcon::OPT(debugPrompt)
|
3445 |
|
|
set slave [tkcon set ::tkcon::OPT(exec)]
|
3446 |
|
|
set event [tkcon set ::tkcon::PRIV(event)]
|
3447 |
|
|
tkcon set ::tkcon::OPT(exec) [tkcon master interp create debugger]
|
3448 |
|
|
tkcon set ::tkcon::PRIV(event) 1
|
3449 |
|
|
}
|
3450 |
|
|
set max $level
|
3451 |
|
|
while 1 {
|
3452 |
|
|
set err {}
|
3453 |
|
|
if {$tkcon} {
|
3454 |
|
|
# tkcon's overload of gets is advanced enough to not need
|
3455 |
|
|
# this, but we get a little better control this way.
|
3456 |
|
|
tkcon evalSlave set level $level
|
3457 |
|
|
tkcon prompt
|
3458 |
|
|
set line [tkcon getcommand]
|
3459 |
|
|
tkcon console mark set output end
|
3460 |
|
|
} else {
|
3461 |
|
|
puts -nonewline stderr "(level \#$level) debug > "
|
3462 |
|
|
gets stdin line
|
3463 |
|
|
while {![info complete $line]} {
|
3464 |
|
|
puts -nonewline "> "
|
3465 |
|
|
append line "\n[gets stdin]"
|
3466 |
|
|
}
|
3467 |
|
|
}
|
3468 |
|
|
if {[string match {} $line]} continue
|
3469 |
|
|
set key [lindex $line 0]
|
3470 |
|
|
if {![regexp {^([#-]?[0-9]+)} [lreplace $line 0 0] lvl]} {
|
3471 |
|
|
set lvl \#$level
|
3472 |
|
|
}
|
3473 |
|
|
set res {}; set c 0
|
3474 |
|
|
switch -- $key {
|
3475 |
|
|
+ {
|
3476 |
|
|
## Allow for jumping multiple levels
|
3477 |
|
|
if {$level < $max} {
|
3478 |
|
|
idebug trace [incr level] $level 0 VERBOSE
|
3479 |
|
|
}
|
3480 |
|
|
}
|
3481 |
|
|
- {
|
3482 |
|
|
## Allow for jumping multiple levels
|
3483 |
|
|
if {$level > 1} {
|
3484 |
|
|
idebug trace [incr level -1] $level 0 VERBOSE
|
3485 |
|
|
}
|
3486 |
|
|
}
|
3487 |
|
|
. { set c [catch {idebug trace $level $level 0 VERBOSE} res] }
|
3488 |
|
|
v { set c [catch {idebug show vars $lvl } res] }
|
3489 |
|
|
V { set c [catch {idebug show vars $lvl VERBOSE} res] }
|
3490 |
|
|
l { set c [catch {idebug show locals $lvl } res] }
|
3491 |
|
|
L { set c [catch {idebug show locals $lvl VERBOSE} res] }
|
3492 |
|
|
g { set c [catch {idebug show globals $lvl } res] }
|
3493 |
|
|
G { set c [catch {idebug show globals $lvl VERBOSE} res] }
|
3494 |
|
|
t { set c [catch {idebug trace 1 $max $level } res] }
|
3495 |
|
|
T { set c [catch {idebug trace 1 $max $level VERBOSE} res]}
|
3496 |
|
|
b { set c [catch {idebug body $lvl} res] }
|
3497 |
|
|
o { set res [set IDEBUG(on) [expr {!$IDEBUG(on)}]] }
|
3498 |
|
|
h - ? {
|
3499 |
|
|
puts stderr " + Move down in call stack
|
3500 |
|
|
- Move up in call stack
|
3501 |
|
|
. Show current proc name and params
|
3502 |
|
|
|
3503 |
|
|
v Show names of variables currently in scope
|
3504 |
|
|
V Show names of variables currently in scope with values
|
3505 |
|
|
l Show names of local (transient) variables
|
3506 |
|
|
L Show names of local (transient) variables with values
|
3507 |
|
|
g Show names of declared global variables
|
3508 |
|
|
G Show names of declared global variables with values
|
3509 |
|
|
t Show a stack trace
|
3510 |
|
|
T Show a verbose stack trace
|
3511 |
|
|
|
3512 |
|
|
b Show body of current proc
|
3513 |
|
|
o Toggle on/off any further debugging
|
3514 |
|
|
c,q Continue regular execution (Quit debugger)
|
3515 |
|
|
h,? Print this help
|
3516 |
|
|
default Evaluate line at current level (\#$level)"
|
3517 |
|
|
}
|
3518 |
|
|
c - q break
|
3519 |
|
|
default { set c [catch {uplevel \#$level $line} res] }
|
3520 |
|
|
}
|
3521 |
|
|
if {$tkcon} {
|
3522 |
|
|
tkcon set ::tkcon::PRIV(event) \
|
3523 |
|
|
[tkcon evalSlave eval history add [list $line]\
|
3524 |
|
|
\; history nextid]
|
3525 |
|
|
}
|
3526 |
|
|
if {$c} {
|
3527 |
|
|
puts stderr $res
|
3528 |
|
|
} elseif {[string compare {} $res]} {
|
3529 |
|
|
puts $res
|
3530 |
|
|
}
|
3531 |
|
|
}
|
3532 |
|
|
set IDEBUG(debugging) 0
|
3533 |
|
|
if {$tkcon} {
|
3534 |
|
|
tkcon master interp delete debugger
|
3535 |
|
|
tkcon master eval set ::tkcon::OPT(prompt1) \$::tkcon::OPT(prompt2)
|
3536 |
|
|
tkcon set ::tkcon::OPT(exec) $slave
|
3537 |
|
|
tkcon set ::tkcon::PRIV(event) $event
|
3538 |
|
|
tkcon prompt
|
3539 |
|
|
}
|
3540 |
|
|
}
|
3541 |
|
|
bo* {
|
3542 |
|
|
if {[regexp {^([#-]?[0-9]+)} $args level]} {
|
3543 |
|
|
return [uplevel $level {dump c -no [lindex [info level 0] 0]}]
|
3544 |
|
|
}
|
3545 |
|
|
}
|
3546 |
|
|
t* {
|
3547 |
|
|
if {[llength $args]<2} return
|
3548 |
|
|
set min [set max [set lvl $level]]
|
3549 |
|
|
set exp {^#?([0-9]+)? ?#?([0-9]+) ?#?([0-9]+)? ?(VERBOSE)?}
|
3550 |
|
|
if {![regexp $exp $args junk min max lvl verbose]} return
|
3551 |
|
|
for {set i $max} {
|
3552 |
|
|
$i>=$min && ![catch {uplevel \#$i info level 0} info]
|
3553 |
|
|
} {incr i -1} {
|
3554 |
|
|
if {$i==$lvl} {
|
3555 |
|
|
puts -nonewline stderr "* \#$i:\t"
|
3556 |
|
|
} else {
|
3557 |
|
|
puts -nonewline stderr " \#$i:\t"
|
3558 |
|
|
}
|
3559 |
|
|
set name [lindex $info 0]
|
3560 |
|
|
if {[string compare VERBOSE $verbose] || \
|
3561 |
|
|
![llength [info procs $name]]} {
|
3562 |
|
|
puts $info
|
3563 |
|
|
} else {
|
3564 |
|
|
puts "proc $name {[info args $name]} { ... }"
|
3565 |
|
|
set idx 0
|
3566 |
|
|
foreach arg [info args $name] {
|
3567 |
|
|
if {[string match args $arg]} {
|
3568 |
|
|
puts "\t$arg = [lrange $info [incr idx] end]"
|
3569 |
|
|
break
|
3570 |
|
|
} else {
|
3571 |
|
|
puts "\t$arg = [lindex $info [incr idx]]"
|
3572 |
|
|
}
|
3573 |
|
|
}
|
3574 |
|
|
}
|
3575 |
|
|
}
|
3576 |
|
|
}
|
3577 |
|
|
s* {
|
3578 |
|
|
#var, local, global
|
3579 |
|
|
set level \#$level
|
3580 |
|
|
if {![regexp {^([vgl][^ ]*) ?([#-]?[0-9]+)? ?(VERBOSE)?} \
|
3581 |
|
|
$args junk type level verbose]} return
|
3582 |
|
|
switch -glob -- $type {
|
3583 |
|
|
v* { set vars [uplevel $level {lsort [info vars]}] }
|
3584 |
|
|
l* { set vars [uplevel $level {lsort [info locals]}] }
|
3585 |
|
|
g* { set vars [lremove [uplevel $level {info vars}] \
|
3586 |
|
|
[uplevel $level {info locals}]] }
|
3587 |
|
|
}
|
3588 |
|
|
if {[string match VERBOSE $verbose]} {
|
3589 |
|
|
return [uplevel $level dump var -nocomplain $vars]
|
3590 |
|
|
} else {
|
3591 |
|
|
return $vars
|
3592 |
|
|
}
|
3593 |
|
|
}
|
3594 |
|
|
e* - pu* {
|
3595 |
|
|
if {[llength $opt]==1 && [catch {lindex [info level -1] 0} id]} {
|
3596 |
|
|
set id [lindex [info level 0] 0]
|
3597 |
|
|
} else {
|
3598 |
|
|
set id [lindex $opt 1]
|
3599 |
|
|
}
|
3600 |
|
|
if {$IDEBUG(on) && [string match $IDEBUG(id) $id]} {
|
3601 |
|
|
if {[string match e* $opt]} {
|
3602 |
|
|
puts [concat $args]
|
3603 |
|
|
} else { eval puts $args }
|
3604 |
|
|
}
|
3605 |
|
|
}
|
3606 |
|
|
default {
|
3607 |
|
|
return -code error "bad [lindex [info level 0] 0] option \"$opt\",\
|
3608 |
|
|
must be: [join [lsort [list on off id break print body\
|
3609 |
|
|
trace show puts echo]] {, }]"
|
3610 |
|
|
}
|
3611 |
|
|
}
|
3612 |
|
|
}
|
3613 |
|
|
|
3614 |
|
|
## observe - like trace, but not
|
3615 |
|
|
# ARGS: opt - option
|
3616 |
|
|
# name - name of variable or command
|
3617 |
|
|
##
|
3618 |
|
|
proc observe {opt name args} {
|
3619 |
|
|
global tcl_observe
|
3620 |
|
|
switch -glob -- $opt {
|
3621 |
|
|
co* {
|
3622 |
|
|
if {[regexp {^(catch|lreplace|set|puts|for|incr|info|uplevel)$} \
|
3623 |
|
|
$name]} {
|
3624 |
|
|
return -code error "cannot observe \"$name\":\
|
3625 |
|
|
infinite eval loop will occur"
|
3626 |
|
|
}
|
3627 |
|
|
set old ${name}@
|
3628 |
|
|
while {[llength [info command $old]]} { append old @ }
|
3629 |
|
|
rename $name $old
|
3630 |
|
|
set max 4
|
3631 |
|
|
regexp {^[0-9]+} $args max
|
3632 |
|
|
## idebug trace could be used here
|
3633 |
|
|
proc $name args "
|
3634 |
|
|
for {set i \[info level\]; set max \[expr \[info level\]-$max\]} {
|
3635 |
|
|
\$i>=\$max && !\[catch {uplevel \#\$i info level 0} info\]
|
3636 |
|
|
} {incr i -1} {
|
3637 |
|
|
puts -nonewline stderr \" \#\$i:\t\"
|
3638 |
|
|
puts \$info
|
3639 |
|
|
}
|
3640 |
|
|
uplevel \[lreplace \[info level 0\] 0 0 $old\]
|
3641 |
|
|
"
|
3642 |
|
|
set tcl_observe($name) $old
|
3643 |
|
|
}
|
3644 |
|
|
cd* {
|
3645 |
|
|
if {[info exists tcl_observe($name)] && [catch {
|
3646 |
|
|
rename $name {}
|
3647 |
|
|
rename $tcl_observe($name) $name
|
3648 |
|
|
unset tcl_observe($name)
|
3649 |
|
|
} err]} { return -code error $err }
|
3650 |
|
|
}
|
3651 |
|
|
ci* {
|
3652 |
|
|
## What a useless method...
|
3653 |
|
|
if {[info exists tcl_observe($name)]} {
|
3654 |
|
|
set i $tcl_observe($name)
|
3655 |
|
|
set res "\"$name\" observes true command \"$i\""
|
3656 |
|
|
while {[info exists tcl_observe($i)]} {
|
3657 |
|
|
append res "\n\"$name\" observes true command \"$i\""
|
3658 |
|
|
set i $tcl_observe($name)
|
3659 |
|
|
}
|
3660 |
|
|
return $res
|
3661 |
|
|
}
|
3662 |
|
|
}
|
3663 |
|
|
va* - vd* {
|
3664 |
|
|
set type [lindex $args 0]
|
3665 |
|
|
set args [lrange $args 1 end]
|
3666 |
|
|
if {![regexp {^[rwu]} $type type]} {
|
3667 |
|
|
return -code error "bad [lindex [info level 0] 0] $opt type\
|
3668 |
|
|
\"$type\", must be: read, write or unset"
|
3669 |
|
|
}
|
3670 |
|
|
if {![llength $args]} { set args observe_var }
|
3671 |
|
|
uplevel 1 [list trace $opt $name $type $args]
|
3672 |
|
|
}
|
3673 |
|
|
vi* {
|
3674 |
|
|
uplevel 1 [list trace vinfo $name]
|
3675 |
|
|
}
|
3676 |
|
|
default {
|
3677 |
|
|
return -code error "bad [lindex [info level 0] 0] option\
|
3678 |
|
|
\"[lindex $args 0]\", must be: [join [lsort \
|
3679 |
|
|
[list command cdelete cinfo variable vdelete vinfo]] {, }]"
|
3680 |
|
|
}
|
3681 |
|
|
}
|
3682 |
|
|
}
|
3683 |
|
|
|
3684 |
|
|
## observe_var - auxilary function for observing vars, called by trace
|
3685 |
|
|
## via observe
|
3686 |
|
|
# ARGS: name - variable name
|
3687 |
|
|
# el - array element name, if any
|
3688 |
|
|
# op - operation type (rwu)
|
3689 |
|
|
##
|
3690 |
|
|
proc observe_var {name el op} {
|
3691 |
|
|
if {[string match u $op]} {
|
3692 |
|
|
if {[string compare {} $el]} {
|
3693 |
|
|
puts "unset \"${name}($el)\""
|
3694 |
|
|
} else {
|
3695 |
|
|
puts "unset \"$name\""
|
3696 |
|
|
}
|
3697 |
|
|
} else {
|
3698 |
|
|
upvar 1 $name $name
|
3699 |
|
|
if {[info exists ${name}($el)]} {
|
3700 |
|
|
puts [dump v ${name}($el)]
|
3701 |
|
|
} else {
|
3702 |
|
|
puts [dump v $name]
|
3703 |
|
|
}
|
3704 |
|
|
}
|
3705 |
|
|
}
|
3706 |
|
|
|
3707 |
|
|
## which - tells you where a command is found
|
3708 |
|
|
# ARGS: cmd - command name
|
3709 |
|
|
# Returns: where command is found (internal / external / unknown)
|
3710 |
|
|
##
|
3711 |
|
|
proc which cmd {
|
3712 |
|
|
## This tries to auto-load a command if not recognized
|
3713 |
|
|
set types [uplevel 1 [list what $cmd 1]]
|
3714 |
|
|
if {[llength $types]} {
|
3715 |
|
|
set out {}
|
3716 |
|
|
|
3717 |
|
|
foreach type $types {
|
3718 |
|
|
switch -- $type {
|
3719 |
|
|
alias { set res "$cmd: aliased to [alias $cmd]" }
|
3720 |
|
|
procedure { set res "$cmd: procedure" }
|
3721 |
|
|
command { set res "$cmd: internal command" }
|
3722 |
|
|
executable { lappend out [auto_execok $cmd] }
|
3723 |
|
|
variable { lappend out "$cmd: $type" }
|
3724 |
|
|
}
|
3725 |
|
|
if {[info exists res]} {
|
3726 |
|
|
global auto_index
|
3727 |
|
|
if {[info exists auto_index($cmd)]} {
|
3728 |
|
|
## This tells you where the command MIGHT have come from -
|
3729 |
|
|
## not true if the command was redefined interactively or
|
3730 |
|
|
## existed before it had to be auto_loaded. This is just
|
3731 |
|
|
## provided as a hint at where it MAY have come from
|
3732 |
|
|
append res " ($auto_index($cmd))"
|
3733 |
|
|
}
|
3734 |
|
|
lappend out $res
|
3735 |
|
|
unset res
|
3736 |
|
|
}
|
3737 |
|
|
}
|
3738 |
|
|
return [join $out \n]
|
3739 |
|
|
} else {
|
3740 |
|
|
return -code error "$cmd: command not found"
|
3741 |
|
|
}
|
3742 |
|
|
}
|
3743 |
|
|
|
3744 |
|
|
## what - tells you what a string is recognized as
|
3745 |
|
|
# ARGS: str - string to id
|
3746 |
|
|
# Returns: id types of command as list
|
3747 |
|
|
##
|
3748 |
|
|
proc what {str {autoload 0}} {
|
3749 |
|
|
set types {}
|
3750 |
|
|
if {[llength [info commands $str]] || ($autoload && \
|
3751 |
|
|
[auto_load $str] && [llength [info commands $str]])} {
|
3752 |
|
|
if {[lsearch -exact [interp aliases] $str] > -1} {
|
3753 |
|
|
lappend types "alias"
|
3754 |
|
|
} elseif {
|
3755 |
|
|
[llength [info procs $str]] ||
|
3756 |
|
|
([string match *::* $str] &&
|
3757 |
|
|
[llength [namespace eval [namespace qualifier $str] \
|
3758 |
|
|
info procs [namespace tail $str]]])
|
3759 |
|
|
} {
|
3760 |
|
|
lappend types "procedure"
|
3761 |
|
|
} else {
|
3762 |
|
|
lappend types "command"
|
3763 |
|
|
}
|
3764 |
|
|
}
|
3765 |
|
|
if {[llength [uplevel 1 info vars $str]]} {
|
3766 |
|
|
upvar 1 $str var
|
3767 |
|
|
if {[array exists var]} {
|
3768 |
|
|
lappend types array variable
|
3769 |
|
|
} else {
|
3770 |
|
|
lappend types scalar variable
|
3771 |
|
|
}
|
3772 |
|
|
}
|
3773 |
|
|
if {[file isdirectory $str]} {
|
3774 |
|
|
lappend types "directory"
|
3775 |
|
|
}
|
3776 |
|
|
if {[file isfile $str]} {
|
3777 |
|
|
lappend types "file"
|
3778 |
|
|
}
|
3779 |
|
|
if {[llength [info commands winfo]] && [winfo exists $str]} {
|
3780 |
|
|
lappend types "widget"
|
3781 |
|
|
}
|
3782 |
|
|
if {[string compare {} [auto_execok $str]]} {
|
3783 |
|
|
lappend types "executable"
|
3784 |
|
|
}
|
3785 |
|
|
return $types
|
3786 |
|
|
}
|
3787 |
|
|
|
3788 |
|
|
## dir - directory list
|
3789 |
|
|
# ARGS: args - names/glob patterns of directories to list
|
3790 |
|
|
# OPTS: -all - list hidden files as well (Unix dot files)
|
3791 |
|
|
# -long - list in full format "permissions size date filename"
|
3792 |
|
|
# -full - displays / after directories and link paths for links
|
3793 |
|
|
# Returns: a directory listing
|
3794 |
|
|
##
|
3795 |
|
|
proc dir {args} {
|
3796 |
|
|
array set s {
|
3797 |
|
|
all 0 full 0 long 0
|
3798 |
|
|
|
3799 |
|
|
}
|
3800 |
|
|
while {[string match \-* [lindex $args 0]]} {
|
3801 |
|
|
set str [lindex $args 0]
|
3802 |
|
|
set args [lreplace $args 0 0]
|
3803 |
|
|
switch -glob -- $str {
|
3804 |
|
|
-a* {set s(all) 1} -f* {set s(full) 1}
|
3805 |
|
|
-l* {set s(long) 1} -- break
|
3806 |
|
|
default {
|
3807 |
|
|
return -code error "unknown option \"$str\",\
|
3808 |
|
|
should be one of: -all, -full, -long"
|
3809 |
|
|
}
|
3810 |
|
|
}
|
3811 |
|
|
}
|
3812 |
|
|
set sep [string trim [file join . .] .]
|
3813 |
|
|
if {![llength $args]} { set args . }
|
3814 |
|
|
if {$::tcl_version >= 8.3} {
|
3815 |
|
|
# Newer glob args allow safer dir processing. The user may still
|
3816 |
|
|
# want glob chars, but really only for file matching.
|
3817 |
|
|
foreach arg $args {
|
3818 |
|
|
if {[file isdirectory $arg]} {
|
3819 |
|
|
if {$s(all)} {
|
3820 |
|
|
lappend out [list $arg [lsort \
|
3821 |
|
|
[glob -nocomplain -directory $arg .* *]]]
|
3822 |
|
|
} else {
|
3823 |
|
|
lappend out [list $arg [lsort \
|
3824 |
|
|
[glob -nocomplain -directory $arg *]]]
|
3825 |
|
|
}
|
3826 |
|
|
} else {
|
3827 |
|
|
set dir [file dirname $arg]
|
3828 |
|
|
lappend out [list $dir$sep [lsort \
|
3829 |
|
|
[glob -nocomplain -directory $dir [file tail $arg]]]]
|
3830 |
|
|
}
|
3831 |
|
|
}
|
3832 |
|
|
} else {
|
3833 |
|
|
foreach arg $args {
|
3834 |
|
|
if {[file isdirectory $arg]} {
|
3835 |
|
|
set arg [string trimright $arg $sep]$sep
|
3836 |
|
|
if {$s(all)} {
|
3837 |
|
|
lappend out [list $arg [lsort [glob -nocomplain -- $arg.* $arg*]]]
|
3838 |
|
|
} else {
|
3839 |
|
|
lappend out [list $arg [lsort [glob -nocomplain -- $arg*]]]
|
3840 |
|
|
}
|
3841 |
|
|
} else {
|
3842 |
|
|
lappend out [list [file dirname $arg]$sep \
|
3843 |
|
|
[lsort [glob -nocomplain -- $arg]]]
|
3844 |
|
|
}
|
3845 |
|
|
}
|
3846 |
|
|
}
|
3847 |
|
|
if {$s(long)} {
|
3848 |
|
|
set old [clock scan {1 year ago}]
|
3849 |
|
|
set fmt "%s%9d %s %s\n"
|
3850 |
|
|
foreach o $out {
|
3851 |
|
|
set d [lindex $o 0]
|
3852 |
|
|
append res $d:\n
|
3853 |
|
|
foreach f [lindex $o 1] {
|
3854 |
|
|
file lstat $f st
|
3855 |
|
|
set f [file tail $f]
|
3856 |
|
|
if {$s(full)} {
|
3857 |
|
|
switch -glob $st(type) {
|
3858 |
|
|
d* { append f $sep }
|
3859 |
|
|
l* { append f "@ -> [file readlink $d$sep$f]" }
|
3860 |
|
|
default { if {[file exec $d$sep$f]} { append f * } }
|
3861 |
|
|
}
|
3862 |
|
|
}
|
3863 |
|
|
if {[string match file $st(type)]} {
|
3864 |
|
|
set mode -
|
3865 |
|
|
} else {
|
3866 |
|
|
set mode [string index $st(type) 0]
|
3867 |
|
|
}
|
3868 |
|
|
foreach j [split [format %03o [expr {$st(mode)&0777}]] {}] {
|
3869 |
|
|
append mode $s($j)
|
3870 |
|
|
}
|
3871 |
|
|
if {$st(mtime)>$old} {
|
3872 |
|
|
set cfmt {%b %d %H:%M}
|
3873 |
|
|
} else {
|
3874 |
|
|
set cfmt {%b %d %Y}
|
3875 |
|
|
}
|
3876 |
|
|
append res [format $fmt $mode $st(size) \
|
3877 |
|
|
[clock format $st(mtime) -format $cfmt] $f]
|
3878 |
|
|
}
|
3879 |
|
|
append res \n
|
3880 |
|
|
}
|
3881 |
|
|
} else {
|
3882 |
|
|
foreach o $out {
|
3883 |
|
|
set d [lindex $o 0]
|
3884 |
|
|
append res "$d:\n"
|
3885 |
|
|
set i 0
|
3886 |
|
|
foreach f [lindex $o 1] {
|
3887 |
|
|
if {[string len [file tail $f]] > $i} {
|
3888 |
|
|
set i [string len [file tail $f]]
|
3889 |
|
|
}
|
3890 |
|
|
}
|
3891 |
|
|
set i [expr {$i+2+$s(full)}]
|
3892 |
|
|
set j 80
|
3893 |
|
|
## This gets the number of cols in the tkcon console widget
|
3894 |
|
|
if {[llength [info commands tkcon]]} {
|
3895 |
|
|
set j [expr {[tkcon master set ::tkcon::OPT(cols)]/$i}]
|
3896 |
|
|
}
|
3897 |
|
|
set k 0
|
3898 |
|
|
foreach f [lindex $o 1] {
|
3899 |
|
|
set f [file tail $f]
|
3900 |
|
|
if {$s(full)} {
|
3901 |
|
|
switch -glob [file type $d$sep$f] {
|
3902 |
|
|
d* { append f $sep }
|
3903 |
|
|
l* { append f @ }
|
3904 |
|
|
default { if {[file exec $d$sep$f]} { append f * } }
|
3905 |
|
|
}
|
3906 |
|
|
}
|
3907 |
|
|
append res [format "%-${i}s" $f]
|
3908 |
|
|
if {$j == 0 || [incr k]%$j == 0} {
|
3909 |
|
|
set res [string trimright $res]\n
|
3910 |
|
|
}
|
3911 |
|
|
}
|
3912 |
|
|
append res \n\n
|
3913 |
|
|
}
|
3914 |
|
|
}
|
3915 |
|
|
return [string trimright $res]
|
3916 |
|
|
}
|
3917 |
|
|
interp alias {} ::ls {} ::dir -full
|
3918 |
|
|
|
3919 |
|
|
## lremove - remove items from a list
|
3920 |
|
|
# OPTS:
|
3921 |
|
|
# -all remove all instances of each item
|
3922 |
|
|
# -glob remove all instances matching glob pattern
|
3923 |
|
|
# -regexp remove all instances matching regexp pattern
|
3924 |
|
|
# ARGS: l a list to remove items from
|
3925 |
|
|
# args items to remove (these are 'join'ed together)
|
3926 |
|
|
##
|
3927 |
|
|
proc lremove {args} {
|
3928 |
|
|
array set opts {-all 0 pattern -exact}
|
3929 |
|
|
while {[string match -* [lindex $args 0]]} {
|
3930 |
|
|
switch -glob -- [lindex $args 0] {
|
3931 |
|
|
-a* { set opts(-all) 1 }
|
3932 |
|
|
-g* { set opts(pattern) -glob }
|
3933 |
|
|
-r* { set opts(pattern) -regexp }
|
3934 |
|
|
-- { set args [lreplace $args 0 0]; break }
|
3935 |
|
|
default {return -code error "unknown option \"[lindex $args 0]\""}
|
3936 |
|
|
}
|
3937 |
|
|
set args [lreplace $args 0 0]
|
3938 |
|
|
}
|
3939 |
|
|
set l [lindex $args 0]
|
3940 |
|
|
foreach i [join [lreplace $args 0 0]] {
|
3941 |
|
|
if {[set ix [lsearch $opts(pattern) $l $i]] == -1} continue
|
3942 |
|
|
set l [lreplace $l $ix $ix]
|
3943 |
|
|
if {$opts(-all)} {
|
3944 |
|
|
while {[set ix [lsearch $opts(pattern) $l $i]] != -1} {
|
3945 |
|
|
set l [lreplace $l $ix $ix]
|
3946 |
|
|
}
|
3947 |
|
|
}
|
3948 |
|
|
}
|
3949 |
|
|
return $l
|
3950 |
|
|
}
|
3951 |
|
|
|
3952 |
|
|
if {!$::tkcon::PRIV(WWW)} {;
|
3953 |
|
|
|
3954 |
|
|
## Unknown changed to get output into tkcon window
|
3955 |
|
|
# unknown:
|
3956 |
|
|
# Invoked automatically whenever an unknown command is encountered.
|
3957 |
|
|
# Works through a list of "unknown handlers" that have been registered
|
3958 |
|
|
# to deal with unknown commands. Extensions can integrate their own
|
3959 |
|
|
# handlers into the 'unknown' facility via 'unknown_handler'.
|
3960 |
|
|
#
|
3961 |
|
|
# If a handler exists that recognizes the command, then it will
|
3962 |
|
|
# take care of the command action and return a valid result or a
|
3963 |
|
|
# Tcl error. Otherwise, it should return "-code continue" (=2)
|
3964 |
|
|
# and responsibility for the command is passed to the next handler.
|
3965 |
|
|
#
|
3966 |
|
|
# Arguments:
|
3967 |
|
|
# args - A list whose elements are the words of the original
|
3968 |
|
|
# command, including the command name.
|
3969 |
|
|
|
3970 |
|
|
proc unknown args {
|
3971 |
|
|
global unknown_handler_order unknown_handlers errorInfo errorCode
|
3972 |
|
|
|
3973 |
|
|
#
|
3974 |
|
|
# Be careful to save error info now, and restore it later
|
3975 |
|
|
# for each handler. Some handlers generate their own errors
|
3976 |
|
|
# and disrupt handling.
|
3977 |
|
|
#
|
3978 |
|
|
set savedErrorCode $errorCode
|
3979 |
|
|
set savedErrorInfo $errorInfo
|
3980 |
|
|
|
3981 |
|
|
if {![info exists unknown_handler_order] || \
|
3982 |
|
|
![info exists unknown_handlers]} {
|
3983 |
|
|
set unknown_handlers(tcl) tcl_unknown
|
3984 |
|
|
set unknown_handler_order tcl
|
3985 |
|
|
}
|
3986 |
|
|
|
3987 |
|
|
foreach handler $unknown_handler_order {
|
3988 |
|
|
set status [catch {uplevel 1 $unknown_handlers($handler) $args} result]
|
3989 |
|
|
|
3990 |
|
|
if {$status == 1} {
|
3991 |
|
|
#
|
3992 |
|
|
# Strip the last five lines off the error stack (they're
|
3993 |
|
|
# from the "uplevel" command).
|
3994 |
|
|
#
|
3995 |
|
|
set new [split $errorInfo \n]
|
3996 |
|
|
set new [join [lrange $new 0 [expr {[llength $new]-6}]] \n]
|
3997 |
|
|
return -code $status -errorcode $errorCode \
|
3998 |
|
|
-errorinfo $new $result
|
3999 |
|
|
|
4000 |
|
|
} elseif {$status != 4} {
|
4001 |
|
|
return -code $status $result
|
4002 |
|
|
}
|
4003 |
|
|
|
4004 |
|
|
set errorCode $savedErrorCode
|
4005 |
|
|
set errorInfo $savedErrorInfo
|
4006 |
|
|
}
|
4007 |
|
|
|
4008 |
|
|
set name [lindex $args 0]
|
4009 |
|
|
return -code error "invalid command name \"$name\""
|
4010 |
|
|
}
|
4011 |
|
|
|
4012 |
|
|
# tcl_unknown:
|
4013 |
|
|
# Invoked when a Tcl command is invoked that doesn't exist in the
|
4014 |
|
|
# interpreter:
|
4015 |
|
|
#
|
4016 |
|
|
# 1. See if the autoload facility can locate the command in a
|
4017 |
|
|
# Tcl script file. If so, load it and execute it.
|
4018 |
|
|
# 2. If the command was invoked interactively at top-level:
|
4019 |
|
|
# (a) see if the command exists as an executable UNIX program.
|
4020 |
|
|
# If so, "exec" the command.
|
4021 |
|
|
# (b) see if the command requests csh-like history substitution
|
4022 |
|
|
# in one of the common forms !!, !<number>, or ^old^new. If
|
4023 |
|
|
# so, emulate csh's history substitution.
|
4024 |
|
|
# (c) see if the command is a unique abbreviation for another
|
4025 |
|
|
# command. If so, invoke the command.
|
4026 |
|
|
#
|
4027 |
|
|
# Arguments:
|
4028 |
|
|
# args - A list whose elements are the words of the original
|
4029 |
|
|
# command, including the command name.
|
4030 |
|
|
|
4031 |
|
|
proc tcl_unknown args {
|
4032 |
|
|
global auto_noexec auto_noload env unknown_pending tcl_interactive
|
4033 |
|
|
global errorCode errorInfo
|
4034 |
|
|
|
4035 |
|
|
# If the command word has the form "namespace inscope ns cmd"
|
4036 |
|
|
# then concatenate its arguments onto the end and evaluate it.
|
4037 |
|
|
|
4038 |
|
|
set cmd [lindex $args 0]
|
4039 |
|
|
if {[regexp "^namespace\[ \t\n\]+inscope" $cmd] && [llength $cmd] == 4} {
|
4040 |
|
|
set arglist [lrange $args 1 end]
|
4041 |
|
|
set ret [catch {uplevel 1 $cmd $arglist} result]
|
4042 |
|
|
if {$ret == 0} {
|
4043 |
|
|
return $result
|
4044 |
|
|
} else {
|
4045 |
|
|
return -code $ret -errorcode $errorCode $result
|
4046 |
|
|
}
|
4047 |
|
|
}
|
4048 |
|
|
|
4049 |
|
|
# Save the values of errorCode and errorInfo variables, since they
|
4050 |
|
|
# may get modified if caught errors occur below. The variables will
|
4051 |
|
|
# be restored just before re-executing the missing command.
|
4052 |
|
|
|
4053 |
|
|
set savedErrorCode $errorCode
|
4054 |
|
|
set savedErrorInfo $errorInfo
|
4055 |
|
|
set name [lindex $args 0]
|
4056 |
|
|
if {![info exists auto_noload]} {
|
4057 |
|
|
#
|
4058 |
|
|
# Make sure we're not trying to load the same proc twice.
|
4059 |
|
|
#
|
4060 |
|
|
if {[info exists unknown_pending($name)]} {
|
4061 |
|
|
return -code error "self-referential recursion in \"unknown\" for command \"$name\""
|
4062 |
|
|
}
|
4063 |
|
|
set unknown_pending($name) pending
|
4064 |
|
|
if {[llength [info args auto_load]]==1} {
|
4065 |
|
|
set ret [catch {auto_load $name} msg]
|
4066 |
|
|
} else {
|
4067 |
|
|
set ret [catch {auto_load $name [uplevel 1 {namespace current}]} msg]
|
4068 |
|
|
}
|
4069 |
|
|
unset unknown_pending($name)
|
4070 |
|
|
if {$ret} {
|
4071 |
|
|
return -code $ret -errorcode $errorCode \
|
4072 |
|
|
"error while autoloading \"$name\": $msg"
|
4073 |
|
|
}
|
4074 |
|
|
if {![array size unknown_pending]} { unset unknown_pending }
|
4075 |
|
|
if {$msg} {
|
4076 |
|
|
set errorCode $savedErrorCode
|
4077 |
|
|
set errorInfo $savedErrorInfo
|
4078 |
|
|
set code [catch {uplevel 1 $args} msg]
|
4079 |
|
|
if {$code == 1} {
|
4080 |
|
|
#
|
4081 |
|
|
# Strip the last five lines off the error stack (they're
|
4082 |
|
|
# from the "uplevel" command).
|
4083 |
|
|
#
|
4084 |
|
|
|
4085 |
|
|
set new [split $errorInfo \n]
|
4086 |
|
|
set new [join [lrange $new 0 [expr {[llength $new]-6}]] \n]
|
4087 |
|
|
return -code error -errorcode $errorCode \
|
4088 |
|
|
-errorinfo $new $msg
|
4089 |
|
|
} else {
|
4090 |
|
|
return -code $code $msg
|
4091 |
|
|
}
|
4092 |
|
|
}
|
4093 |
|
|
}
|
4094 |
|
|
if {[info level] == 1 && [string match {} [info script]] \
|
4095 |
|
|
&& [info exists tcl_interactive] && $tcl_interactive} {
|
4096 |
|
|
if {![info exists auto_noexec]} {
|
4097 |
|
|
set new [auto_execok $name]
|
4098 |
|
|
if {[string compare {} $new]} {
|
4099 |
|
|
set errorCode $savedErrorCode
|
4100 |
|
|
set errorInfo $savedErrorInfo
|
4101 |
|
|
return [uplevel 1 exec $new [lrange $args 1 end]]
|
4102 |
|
|
#return [uplevel exec >&@stdout <@stdin $new [lrange $args 1 end]]
|
4103 |
|
|
}
|
4104 |
|
|
}
|
4105 |
|
|
set errorCode $savedErrorCode
|
4106 |
|
|
set errorInfo $savedErrorInfo
|
4107 |
|
|
##
|
4108 |
|
|
## History substitution moved into ::tkcon::EvalCmd
|
4109 |
|
|
##
|
4110 |
|
|
if {[string compare $name "::"] == 0} {
|
4111 |
|
|
set name ""
|
4112 |
|
|
}
|
4113 |
|
|
if {$ret != 0} {
|
4114 |
|
|
return -code $ret -errorcode $errorCode \
|
4115 |
|
|
"error in unknown while checking if \"$name\" is a unique command abbreviation: $msg"
|
4116 |
|
|
}
|
4117 |
|
|
set cmds [info commands $name*]
|
4118 |
|
|
if {[llength $cmds] == 1} {
|
4119 |
|
|
return [uplevel 1 [lreplace $args 0 0 $cmds]]
|
4120 |
|
|
}
|
4121 |
|
|
if {[llength $cmds]} {
|
4122 |
|
|
if {$name == ""} {
|
4123 |
|
|
return -code error "empty command name \"\""
|
4124 |
|
|
} else {
|
4125 |
|
|
return -code error \
|
4126 |
|
|
"ambiguous command name \"$name\": [lsort $cmds]"
|
4127 |
|
|
}
|
4128 |
|
|
}
|
4129 |
|
|
## We've got nothing so far
|
4130 |
|
|
## Check and see if Tk wasn't loaded, but it appears to be a Tk cmd
|
4131 |
|
|
if {![uplevel \#0 info exists tk_version]} {
|
4132 |
|
|
lappend tkcmds bell bind bindtags button \
|
4133 |
|
|
canvas checkbutton clipboard destroy \
|
4134 |
|
|
entry event focus font frame grab grid image \
|
4135 |
|
|
label listbox lower menu menubutton message \
|
4136 |
|
|
option pack place radiobutton raise \
|
4137 |
|
|
scale scrollbar selection send spinbox \
|
4138 |
|
|
text tk tkwait toplevel winfo wm
|
4139 |
|
|
if {[lsearch -exact $tkcmds $name] >= 0 && \
|
4140 |
|
|
[tkcon master tk_messageBox -icon question -parent . \
|
4141 |
|
|
-title "Load Tk?" -type retrycancel -default retry \
|
4142 |
|
|
-message "This appears to be a Tk command, but Tk\
|
4143 |
|
|
has not yet been loaded. Shall I retry the command\
|
4144 |
|
|
with loading Tk first?"] == "retry"} {
|
4145 |
|
|
return [uplevel 1 "load {} Tk; $args"]
|
4146 |
|
|
}
|
4147 |
|
|
}
|
4148 |
|
|
}
|
4149 |
|
|
return -code continue
|
4150 |
|
|
}
|
4151 |
|
|
|
4152 |
|
|
} ; # end exclusionary code for WWW
|
4153 |
|
|
|
4154 |
|
|
proc ::tkcon::Bindings {} {
|
4155 |
|
|
variable PRIV
|
4156 |
|
|
global tcl_platform tk_version
|
4157 |
|
|
|
4158 |
|
|
#-----------------------------------------------------------------------
|
4159 |
|
|
# Elements of tkPriv that are used in this file:
|
4160 |
|
|
#
|
4161 |
|
|
# char - Character position on the line; kept in order
|
4162 |
|
|
# to allow moving up or down past short lines while
|
4163 |
|
|
# still remembering the desired position.
|
4164 |
|
|
# mouseMoved - Non-zero means the mouse has moved a significant
|
4165 |
|
|
# amount since the button went down (so, for example,
|
4166 |
|
|
# start dragging out a selection).
|
4167 |
|
|
# prevPos - Used when moving up or down lines via the keyboard.
|
4168 |
|
|
# Keeps track of the previous insert position, so
|
4169 |
|
|
# we can distinguish a series of ups and downs, all
|
4170 |
|
|
# in a row, from a new up or down.
|
4171 |
|
|
# selectMode - The style of selection currently underway:
|
4172 |
|
|
# char, word, or line.
|
4173 |
|
|
# x, y - Last known mouse coordinates for scanning
|
4174 |
|
|
# and auto-scanning.
|
4175 |
|
|
#-----------------------------------------------------------------------
|
4176 |
|
|
|
4177 |
|
|
switch -glob $tcl_platform(platform) {
|
4178 |
|
|
win* { set PRIV(meta) Alt }
|
4179 |
|
|
mac* { set PRIV(meta) Command }
|
4180 |
|
|
default { set PRIV(meta) Meta }
|
4181 |
|
|
}
|
4182 |
|
|
|
4183 |
|
|
## Get all Text bindings into TkConsole
|
4184 |
|
|
foreach ev [bind Text] { bind TkConsole $ev [bind Text $ev] }
|
4185 |
|
|
## We really didn't want the newline insertion
|
4186 |
|
|
bind TkConsole <Control-Key-o> {}
|
4187 |
|
|
|
4188 |
|
|
## Now make all our virtual event bindings
|
4189 |
|
|
foreach {ev key} [subst -nocommand -noback {
|
4190 |
|
|
<<TkCon_Exit>> <Control-q>
|
4191 |
|
|
<<TkCon_New>> <Control-N>
|
4192 |
|
|
<<TkCon_Close>> <Control-w>
|
4193 |
|
|
<<TkCon_About>> <Control-A>
|
4194 |
|
|
<<TkCon_Help>> <Control-H>
|
4195 |
|
|
<<TkCon_Find>> <Control-F>
|
4196 |
|
|
<<TkCon_Slave>> <Control-Key-1>
|
4197 |
|
|
<<TkCon_Master>> <Control-Key-2>
|
4198 |
|
|
<<TkCon_Main>> <Control-Key-3>
|
4199 |
|
|
<<TkCon_Expand>> <Key-Tab>
|
4200 |
|
|
<<TkCon_ExpandFile>> <Key-Escape>
|
4201 |
|
|
<<TkCon_ExpandProc>> <Control-P>
|
4202 |
|
|
<<TkCon_ExpandVar>> <Control-V>
|
4203 |
|
|
<<TkCon_Tab>> <Control-i>
|
4204 |
|
|
<<TkCon_Tab>> <$PRIV(meta)-i>
|
4205 |
|
|
<<TkCon_Newline>> <Control-o>
|
4206 |
|
|
<<TkCon_Newline>> <$PRIV(meta)-o>
|
4207 |
|
|
<<TkCon_Newline>> <Control-Key-Return>
|
4208 |
|
|
<<TkCon_Newline>> <Control-Key-KP_Enter>
|
4209 |
|
|
<<TkCon_Eval>> <Return>
|
4210 |
|
|
<<TkCon_Eval>> <KP_Enter>
|
4211 |
|
|
<<TkCon_Clear>> <Control-l>
|
4212 |
|
|
<<TkCon_Previous>> <Up>
|
4213 |
|
|
<<TkCon_PreviousImmediate>> <Control-p>
|
4214 |
|
|
<<TkCon_PreviousSearch>> <Control-r>
|
4215 |
|
|
<<TkCon_Next>> <Down>
|
4216 |
|
|
<<TkCon_NextImmediate>> <Control-n>
|
4217 |
|
|
<<TkCon_NextSearch>> <Control-s>
|
4218 |
|
|
<<TkCon_Transpose>> <Control-t>
|
4219 |
|
|
<<TkCon_ClearLine>> <Control-u>
|
4220 |
|
|
<<TkCon_SaveCommand>> <Control-z>
|
4221 |
|
|
<<TkCon_Popup>> <Button-3>
|
4222 |
|
|
}] {
|
4223 |
|
|
event add $ev $key
|
4224 |
|
|
## Make sure the specific key won't be defined
|
4225 |
|
|
bind TkConsole $key {}
|
4226 |
|
|
}
|
4227 |
|
|
|
4228 |
|
|
## Make the ROOT bindings
|
4229 |
|
|
bind $PRIV(root) <<TkCon_Exit>> exit
|
4230 |
|
|
bind $PRIV(root) <<TkCon_New>> { ::tkcon::New }
|
4231 |
|
|
bind $PRIV(root) <<TkCon_Close>> { ::tkcon::Destroy }
|
4232 |
|
|
bind $PRIV(root) <<TkCon_About>> { ::tkcon::About }
|
4233 |
|
|
bind $PRIV(root) <<TkCon_Help>> { ::tkcon::Help }
|
4234 |
|
|
bind $PRIV(root) <<TkCon_Find>> { ::tkcon::FindBox $::tkcon::PRIV(console) }
|
4235 |
|
|
bind $PRIV(root) <<TkCon_Slave>> {
|
4236 |
|
|
::tkcon::Attach {}
|
4237 |
|
|
::tkcon::Prompt "\n" [::tkcon::CmdGet $::tkcon::PRIV(console)]
|
4238 |
|
|
}
|
4239 |
|
|
bind $PRIV(root) <<TkCon_Master>> {
|
4240 |
|
|
if {[string compare {} $::tkcon::PRIV(name)]} {
|
4241 |
|
|
::tkcon::Attach $::tkcon::PRIV(name)
|
4242 |
|
|
} else {
|
4243 |
|
|
::tkcon::Attach Main
|
4244 |
|
|
}
|
4245 |
|
|
::tkcon::Prompt "\n" [::tkcon::CmdGet $::tkcon::PRIV(console)]
|
4246 |
|
|
}
|
4247 |
|
|
bind $PRIV(root) <<TkCon_Main>> {
|
4248 |
|
|
::tkcon::Attach Main
|
4249 |
|
|
::tkcon::Prompt "\n" [::tkcon::CmdGet $::tkcon::PRIV(console)]
|
4250 |
|
|
}
|
4251 |
|
|
bind $PRIV(root) <<TkCon_Popup>> {
|
4252 |
|
|
::tkcon::PopupMenu %X %Y
|
4253 |
|
|
}
|
4254 |
|
|
|
4255 |
|
|
## Menu items need null TkConsolePost bindings to avoid the TagProc
|
4256 |
|
|
##
|
4257 |
|
|
foreach ev [bind $PRIV(root)] {
|
4258 |
|
|
bind TkConsolePost $ev {
|
4259 |
|
|
# empty
|
4260 |
|
|
}
|
4261 |
|
|
}
|
4262 |
|
|
|
4263 |
|
|
|
4264 |
|
|
# ::tkcon::ClipboardKeysyms --
|
4265 |
|
|
# This procedure is invoked to identify the keys that correspond to
|
4266 |
|
|
# the copy, cut, and paste functions for the clipboard.
|
4267 |
|
|
#
|
4268 |
|
|
# Arguments:
|
4269 |
|
|
# copy - Name of the key (keysym name plus modifiers, if any,
|
4270 |
|
|
# such as "Meta-y") used for the copy operation.
|
4271 |
|
|
# cut - Name of the key used for the cut operation.
|
4272 |
|
|
# paste - Name of the key used for the paste operation.
|
4273 |
|
|
|
4274 |
|
|
proc ::tkcon::ClipboardKeysyms {copy cut paste} {
|
4275 |
|
|
bind TkConsole <$copy> {::tkcon::Copy %W}
|
4276 |
|
|
bind TkConsole <$cut> {::tkcon::Cut %W}
|
4277 |
|
|
bind TkConsole <$paste> {::tkcon::Paste %W}
|
4278 |
|
|
}
|
4279 |
|
|
|
4280 |
|
|
proc ::tkcon::GetSelection {w} {
|
4281 |
|
|
if {
|
4282 |
|
|
![catch {selection get -displayof $w -type UTF8_STRING} txt] ||
|
4283 |
|
|
![catch {selection get -displayof $w} txt] ||
|
4284 |
|
|
![catch {selection get -displayof $w -selection CLIPBOARD} txt]
|
4285 |
|
|
} {
|
4286 |
|
|
return $txt
|
4287 |
|
|
}
|
4288 |
|
|
return -code error "could not find default selection"
|
4289 |
|
|
}
|
4290 |
|
|
|
4291 |
|
|
proc ::tkcon::Cut w {
|
4292 |
|
|
if {[string match $w [selection own -displayof $w]]} {
|
4293 |
|
|
clipboard clear -displayof $w
|
4294 |
|
|
catch {
|
4295 |
|
|
set txt [selection get -displayof $w]
|
4296 |
|
|
clipboard append -displayof $w $txt
|
4297 |
|
|
if {[$w compare sel.first >= limit]} {
|
4298 |
|
|
$w delete sel.first sel.last
|
4299 |
|
|
}
|
4300 |
|
|
}
|
4301 |
|
|
}
|
4302 |
|
|
}
|
4303 |
|
|
proc ::tkcon::Copy w {
|
4304 |
|
|
if {[string match $w [selection own -displayof $w]]} {
|
4305 |
|
|
clipboard clear -displayof $w
|
4306 |
|
|
catch {
|
4307 |
|
|
set txt [selection get -displayof $w]
|
4308 |
|
|
clipboard append -displayof $w $txt
|
4309 |
|
|
}
|
4310 |
|
|
}
|
4311 |
|
|
}
|
4312 |
|
|
proc ::tkcon::Paste w {
|
4313 |
|
|
if {![catch {GetSelection $w} txt]} {
|
4314 |
|
|
if {[$w compare insert < limit]} { $w mark set insert end }
|
4315 |
|
|
$w insert insert $txt
|
4316 |
|
|
$w see insert
|
4317 |
|
|
if {[string match *\n* $txt]} { ::tkcon::Eval $w }
|
4318 |
|
|
}
|
4319 |
|
|
}
|
4320 |
|
|
|
4321 |
|
|
## Redefine for TkConsole what we need
|
4322 |
|
|
##
|
4323 |
|
|
event delete <<Paste>> <Control-V>
|
4324 |
|
|
::tkcon::ClipboardKeysyms <Copy> <Cut> <Paste>
|
4325 |
|
|
|
4326 |
|
|
bind TkConsole <Insert> {
|
4327 |
|
|
catch { ::tkcon::Insert %W [::tkcon::GetSelection %W] }
|
4328 |
|
|
}
|
4329 |
|
|
|
4330 |
|
|
bind TkConsole <Triple-1> {+
|
4331 |
|
|
catch {
|
4332 |
|
|
eval %W tag remove sel [%W tag nextrange prompt sel.first sel.last]
|
4333 |
|
|
eval %W tag remove sel sel.last-1c
|
4334 |
|
|
%W mark set insert sel.first
|
4335 |
|
|
}
|
4336 |
|
|
}
|
4337 |
|
|
|
4338 |
|
|
## binding editor needed
|
4339 |
|
|
## binding <events> for .tkconrc
|
4340 |
|
|
|
4341 |
|
|
bind TkConsole <<TkCon_ExpandFile>> {
|
4342 |
|
|
if {[%W compare insert > limit]} {::tkcon::Expand %W path}
|
4343 |
|
|
break
|
4344 |
|
|
}
|
4345 |
|
|
bind TkConsole <<TkCon_ExpandProc>> {
|
4346 |
|
|
if {[%W compare insert > limit]} {::tkcon::Expand %W proc}
|
4347 |
|
|
}
|
4348 |
|
|
bind TkConsole <<TkCon_ExpandVar>> {
|
4349 |
|
|
if {[%W compare insert > limit]} {::tkcon::Expand %W var}
|
4350 |
|
|
}
|
4351 |
|
|
bind TkConsole <<TkCon_Expand>> {
|
4352 |
|
|
if {[%W compare insert > limit]} {::tkcon::Expand %W}
|
4353 |
|
|
}
|
4354 |
|
|
bind TkConsole <<TkCon_Tab>> {
|
4355 |
|
|
if {[%W compare insert >= limit]} {
|
4356 |
|
|
::tkcon::Insert %W \t
|
4357 |
|
|
}
|
4358 |
|
|
}
|
4359 |
|
|
bind TkConsole <<TkCon_Newline>> {
|
4360 |
|
|
if {[%W compare insert >= limit]} {
|
4361 |
|
|
::tkcon::Insert %W \n
|
4362 |
|
|
}
|
4363 |
|
|
}
|
4364 |
|
|
bind TkConsole <<TkCon_Eval>> {
|
4365 |
|
|
::tkcon::Eval %W
|
4366 |
|
|
}
|
4367 |
|
|
bind TkConsole <Delete> {
|
4368 |
|
|
if {[llength [%W tag nextrange sel 1.0 end]] \
|
4369 |
|
|
&& [%W compare sel.first >= limit]} {
|
4370 |
|
|
%W delete sel.first sel.last
|
4371 |
|
|
} elseif {[%W compare insert >= limit]} {
|
4372 |
|
|
%W delete insert
|
4373 |
|
|
%W see insert
|
4374 |
|
|
}
|
4375 |
|
|
}
|
4376 |
|
|
bind TkConsole <BackSpace> {
|
4377 |
|
|
if {[llength [%W tag nextrange sel 1.0 end]] \
|
4378 |
|
|
&& [%W compare sel.first >= limit]} {
|
4379 |
|
|
%W delete sel.first sel.last
|
4380 |
|
|
} elseif {[%W compare insert != 1.0] && [%W compare insert > limit]} {
|
4381 |
|
|
%W delete insert-1c
|
4382 |
|
|
%W see insert
|
4383 |
|
|
}
|
4384 |
|
|
}
|
4385 |
|
|
bind TkConsole <Control-h> [bind TkConsole <BackSpace>]
|
4386 |
|
|
|
4387 |
|
|
bind TkConsole <KeyPress> {
|
4388 |
|
|
::tkcon::Insert %W %A
|
4389 |
|
|
}
|
4390 |
|
|
|
4391 |
|
|
bind TkConsole <Control-a> {
|
4392 |
|
|
if {[%W compare {limit linestart} == {insert linestart}]} {
|
4393 |
|
|
tkTextSetCursor %W limit
|
4394 |
|
|
} else {
|
4395 |
|
|
tkTextSetCursor %W {insert linestart}
|
4396 |
|
|
}
|
4397 |
|
|
}
|
4398 |
|
|
bind TkConsole <Key-Home> [bind TkConsole <Control-a>]
|
4399 |
|
|
bind TkConsole <Control-d> {
|
4400 |
|
|
if {[%W compare insert < limit]} break
|
4401 |
|
|
%W delete insert
|
4402 |
|
|
}
|
4403 |
|
|
bind TkConsole <Control-k> {
|
4404 |
|
|
if {[%W compare insert < limit]} break
|
4405 |
|
|
if {[%W compare insert == {insert lineend}]} {
|
4406 |
|
|
%W delete insert
|
4407 |
|
|
} else {
|
4408 |
|
|
%W delete insert {insert lineend}
|
4409 |
|
|
}
|
4410 |
|
|
}
|
4411 |
|
|
bind TkConsole <<TkCon_Clear>> {
|
4412 |
|
|
## Clear console buffer, without losing current command line input
|
4413 |
|
|
set ::tkcon::PRIV(tmp) [::tkcon::CmdGet %W]
|
4414 |
|
|
clear
|
4415 |
|
|
::tkcon::Prompt {} $::tkcon::PRIV(tmp)
|
4416 |
|
|
}
|
4417 |
|
|
bind TkConsole <<TkCon_Previous>> {
|
4418 |
|
|
if {[%W compare {insert linestart} != {limit linestart}]} {
|
4419 |
|
|
tkTextSetCursor %W [tkTextUpDownLine %W -1]
|
4420 |
|
|
} else {
|
4421 |
|
|
::tkcon::Event -1
|
4422 |
|
|
}
|
4423 |
|
|
}
|
4424 |
|
|
bind TkConsole <<TkCon_Next>> {
|
4425 |
|
|
if {[%W compare {insert linestart} != {end-1c linestart}]} {
|
4426 |
|
|
tkTextSetCursor %W [tkTextUpDownLine %W 1]
|
4427 |
|
|
} else {
|
4428 |
|
|
::tkcon::Event 1
|
4429 |
|
|
}
|
4430 |
|
|
}
|
4431 |
|
|
bind TkConsole <<TkCon_NextImmediate>> { ::tkcon::Event 1 }
|
4432 |
|
|
bind TkConsole <<TkCon_PreviousImmediate>> { ::tkcon::Event -1 }
|
4433 |
|
|
bind TkConsole <<TkCon_PreviousSearch>> {
|
4434 |
|
|
::tkcon::Event -1 [::tkcon::CmdGet %W]
|
4435 |
|
|
}
|
4436 |
|
|
bind TkConsole <<TkCon_NextSearch>> {
|
4437 |
|
|
::tkcon::Event 1 [::tkcon::CmdGet %W]
|
4438 |
|
|
}
|
4439 |
|
|
bind TkConsole <<TkCon_Transpose>> {
|
4440 |
|
|
## Transpose current and previous chars
|
4441 |
|
|
if {[%W compare insert > "limit+1c"]} { tkTextTranspose %W }
|
4442 |
|
|
}
|
4443 |
|
|
bind TkConsole <<TkCon_ClearLine>> {
|
4444 |
|
|
## Clear command line (Unix shell staple)
|
4445 |
|
|
%W delete limit end
|
4446 |
|
|
}
|
4447 |
|
|
bind TkConsole <<TkCon_SaveCommand>> {
|
4448 |
|
|
## Save command buffer (swaps with current command)
|
4449 |
|
|
set ::tkcon::PRIV(tmp) $::tkcon::PRIV(cmdsave)
|
4450 |
|
|
set ::tkcon::PRIV(cmdsave) [::tkcon::CmdGet %W]
|
4451 |
|
|
if {[string match {} $::tkcon::PRIV(cmdsave)]} {
|
4452 |
|
|
set ::tkcon::PRIV(cmdsave) $::tkcon::PRIV(tmp)
|
4453 |
|
|
} else {
|
4454 |
|
|
%W delete limit end-1c
|
4455 |
|
|
}
|
4456 |
|
|
::tkcon::Insert %W $::tkcon::PRIV(tmp)
|
4457 |
|
|
%W see end
|
4458 |
|
|
}
|
4459 |
|
|
catch {bind TkConsole <Key-Page_Up> { tkTextScrollPages %W -1 }}
|
4460 |
|
|
catch {bind TkConsole <Key-Prior> { tkTextScrollPages %W -1 }}
|
4461 |
|
|
catch {bind TkConsole <Key-Page_Down> { tkTextScrollPages %W 1 }}
|
4462 |
|
|
catch {bind TkConsole <Key-Next> { tkTextScrollPages %W 1 }}
|
4463 |
|
|
bind TkConsole <$PRIV(meta)-d> {
|
4464 |
|
|
if {[%W compare insert >= limit]} {
|
4465 |
|
|
%W delete insert {insert wordend}
|
4466 |
|
|
}
|
4467 |
|
|
}
|
4468 |
|
|
bind TkConsole <$PRIV(meta)-BackSpace> {
|
4469 |
|
|
if {[%W compare {insert -1c wordstart} >= limit]} {
|
4470 |
|
|
%W delete {insert -1c wordstart} insert
|
4471 |
|
|
}
|
4472 |
|
|
}
|
4473 |
|
|
bind TkConsole <$PRIV(meta)-Delete> {
|
4474 |
|
|
if {[%W compare insert >= limit]} {
|
4475 |
|
|
%W delete insert {insert wordend}
|
4476 |
|
|
}
|
4477 |
|
|
}
|
4478 |
|
|
bind TkConsole <ButtonRelease-2> {
|
4479 |
|
|
if {
|
4480 |
|
|
(!$tkPriv(mouseMoved) || $tk_strictMotif) &&
|
4481 |
|
|
![catch {::tkcon::GetSelection %W} ::tkcon::PRIV(tmp)]
|
4482 |
|
|
} {
|
4483 |
|
|
if {[%W compare @%x,%y < limit]} {
|
4484 |
|
|
%W insert end $::tkcon::PRIV(tmp)
|
4485 |
|
|
} else {
|
4486 |
|
|
%W insert @%x,%y $::tkcon::PRIV(tmp)
|
4487 |
|
|
}
|
4488 |
|
|
if {[string match *\n* $::tkcon::PRIV(tmp)]} {::tkcon::Eval %W}
|
4489 |
|
|
}
|
4490 |
|
|
}
|
4491 |
|
|
|
4492 |
|
|
##
|
4493 |
|
|
## End TkConsole bindings
|
4494 |
|
|
##
|
4495 |
|
|
|
4496 |
|
|
##
|
4497 |
|
|
## Bindings for doing special things based on certain keys
|
4498 |
|
|
##
|
4499 |
|
|
bind TkConsolePost <Key-parenright> {
|
4500 |
|
|
if {$::tkcon::OPT(lightbrace) && $::tkcon::OPT(blinktime)>99 && \
|
4501 |
|
|
[string compare \\ [%W get insert-2c]]} {
|
4502 |
|
|
::tkcon::MatchPair %W \( \) limit
|
4503 |
|
|
}
|
4504 |
|
|
set ::tkcon::PRIV(StatusCursor) [%W index insert]
|
4505 |
|
|
}
|
4506 |
|
|
bind TkConsolePost <Key-bracketright> {
|
4507 |
|
|
if {$::tkcon::OPT(lightbrace) && $::tkcon::OPT(blinktime)>99 && \
|
4508 |
|
|
[string compare \\ [%W get insert-2c]]} {
|
4509 |
|
|
::tkcon::MatchPair %W \[ \] limit
|
4510 |
|
|
}
|
4511 |
|
|
set ::tkcon::PRIV(StatusCursor) [%W index insert]
|
4512 |
|
|
}
|
4513 |
|
|
bind TkConsolePost <Key-braceright> {
|
4514 |
|
|
if {$::tkcon::OPT(lightbrace) && $::tkcon::OPT(blinktime)>99 && \
|
4515 |
|
|
[string compare \\ [%W get insert-2c]]} {
|
4516 |
|
|
::tkcon::MatchPair %W \{ \} limit
|
4517 |
|
|
}
|
4518 |
|
|
set ::tkcon::PRIV(StatusCursor) [%W index insert]
|
4519 |
|
|
}
|
4520 |
|
|
bind TkConsolePost <Key-quotedbl> {
|
4521 |
|
|
if {$::tkcon::OPT(lightbrace) && $::tkcon::OPT(blinktime)>99 && \
|
4522 |
|
|
[string compare \\ [%W get insert-2c]]} {
|
4523 |
|
|
::tkcon::MatchQuote %W limit
|
4524 |
|
|
}
|
4525 |
|
|
set ::tkcon::PRIV(StatusCursor) [%W index insert]
|
4526 |
|
|
}
|
4527 |
|
|
|
4528 |
|
|
bind TkConsolePost <KeyPress> {
|
4529 |
|
|
if {$::tkcon::OPT(lightcmd) && [string compare {} %A]} {
|
4530 |
|
|
::tkcon::TagProc %W
|
4531 |
|
|
}
|
4532 |
|
|
set ::tkcon::PRIV(StatusCursor) [%W index insert]
|
4533 |
|
|
}
|
4534 |
|
|
|
4535 |
|
|
bind TkConsolePost <Button-1> {
|
4536 |
|
|
set ::tkcon::PRIV(StatusCursor) [%W index insert]
|
4537 |
|
|
}
|
4538 |
|
|
bind TkConsolePost <B1-Motion> {
|
4539 |
|
|
set ::tkcon::PRIV(StatusCursor) [%W index insert]
|
4540 |
|
|
}
|
4541 |
|
|
|
4542 |
|
|
}
|
4543 |
|
|
|
4544 |
|
|
##
|
4545 |
|
|
# ::tkcon::PopupMenu - what to do when the popup menu is requested
|
4546 |
|
|
##
|
4547 |
|
|
proc ::tkcon::PopupMenu {X Y} {
|
4548 |
|
|
variable PRIV
|
4549 |
|
|
|
4550 |
|
|
set w $PRIV(console)
|
4551 |
|
|
if {[string compare $w [winfo containing $X $Y]]} {
|
4552 |
|
|
tk_popup $PRIV(popup) $X $Y
|
4553 |
|
|
return
|
4554 |
|
|
}
|
4555 |
|
|
set x [expr {$X-[winfo rootx $w]}]
|
4556 |
|
|
set y [expr {$Y-[winfo rooty $w]}]
|
4557 |
|
|
if {[llength [set tags [$w tag names @$x,$y]]]} {
|
4558 |
|
|
if {[lsearch -exact $tags "proc"] >= 0} {
|
4559 |
|
|
lappend type "proc"
|
4560 |
|
|
foreach {first last} [$w tag prevrange proc @$x,$y] {
|
4561 |
|
|
set word [$w get $first $last]; break
|
4562 |
|
|
}
|
4563 |
|
|
}
|
4564 |
|
|
if {[lsearch -exact $tags "var"] >= 0} {
|
4565 |
|
|
lappend type "var"
|
4566 |
|
|
foreach {first last} [$w tag prevrange var @$x,$y] {
|
4567 |
|
|
set word [$w get $first $last]; break
|
4568 |
|
|
}
|
4569 |
|
|
}
|
4570 |
|
|
}
|
4571 |
|
|
if {![info exists type]} {
|
4572 |
|
|
set exp "(^|\[^\\\\\]\[ \t\n\r\])"
|
4573 |
|
|
set exp2 "\[\[\\\\\\?\\*\]"
|
4574 |
|
|
set i [$w search -backwards -regexp $exp @$x,$y "@$x,$y linestart"]
|
4575 |
|
|
if {[string compare {} $i]} {
|
4576 |
|
|
if {![string match *.0 $i]} {append i +2c}
|
4577 |
|
|
if {[string compare {} \
|
4578 |
|
|
[set j [$w search -regexp $exp $i "$i lineend"]]]} {
|
4579 |
|
|
append j +1c
|
4580 |
|
|
} else {
|
4581 |
|
|
set j "$i lineend"
|
4582 |
|
|
}
|
4583 |
|
|
regsub -all $exp2 [$w get $i $j] {\\\0} word
|
4584 |
|
|
set word [string trim $word {\"$[]{}',?#*}]
|
4585 |
|
|
if {[llength [EvalAttached [list info commands $word]]]} {
|
4586 |
|
|
lappend type "proc"
|
4587 |
|
|
}
|
4588 |
|
|
if {[llength [EvalAttached [list info vars $word]]]} {
|
4589 |
|
|
lappend type "var"
|
4590 |
|
|
}
|
4591 |
|
|
if {[EvalAttached [list file isfile $word]]} {
|
4592 |
|
|
lappend type "file"
|
4593 |
|
|
}
|
4594 |
|
|
}
|
4595 |
|
|
}
|
4596 |
|
|
if {![info exists type] || ![info exists word]} {
|
4597 |
|
|
tk_popup $PRIV(popup) $X $Y
|
4598 |
|
|
return
|
4599 |
|
|
}
|
4600 |
|
|
$PRIV(context) delete 0 end
|
4601 |
|
|
$PRIV(context) add command -label "$word" -state disabled
|
4602 |
|
|
$PRIV(context) add separator
|
4603 |
|
|
set app [Attach]
|
4604 |
|
|
if {[lsearch $type proc] != -1} {
|
4605 |
|
|
$PRIV(context) add command -label "View Procedure" \
|
4606 |
|
|
-command [list edit -attach $app -type proc -- $word]
|
4607 |
|
|
}
|
4608 |
|
|
if {[lsearch $type var] != -1} {
|
4609 |
|
|
$PRIV(context) add command -label "View Variable" \
|
4610 |
|
|
-command [list edit -attach $app -type var -- $word]
|
4611 |
|
|
}
|
4612 |
|
|
if {[lsearch $type file] != -1} {
|
4613 |
|
|
$PRIV(context) add command -label "View File" \
|
4614 |
|
|
-command [list edit -attach $app -type file -- $word]
|
4615 |
|
|
}
|
4616 |
|
|
tk_popup $PRIV(context) $X $Y
|
4617 |
|
|
}
|
4618 |
|
|
|
4619 |
|
|
## ::tkcon::TagProc - tags a procedure in the console if it's recognized
|
4620 |
|
|
## This procedure is not perfect. However, making it perfect wastes
|
4621 |
|
|
## too much CPU time...
|
4622 |
|
|
##
|
4623 |
|
|
proc ::tkcon::TagProc w {
|
4624 |
|
|
set exp "\[^\\\\\]\[\[ \t\n\r\;{}\"\$\]"
|
4625 |
|
|
set i [$w search -backwards -regexp $exp insert-1c limit-1c]
|
4626 |
|
|
if {[string compare {} $i]} {append i +2c} else {set i limit}
|
4627 |
|
|
regsub -all "\[\[\\\\\\?\\*\]" [$w get $i "insert-1c wordend"] {\\\0} c
|
4628 |
|
|
if {[llength [EvalAttached [list info commands $c]]]} {
|
4629 |
|
|
$w tag add proc $i "insert-1c wordend"
|
4630 |
|
|
} else {
|
4631 |
|
|
$w tag remove proc $i "insert-1c wordend"
|
4632 |
|
|
}
|
4633 |
|
|
if {[llength [EvalAttached [list info vars $c]]]} {
|
4634 |
|
|
$w tag add var $i "insert-1c wordend"
|
4635 |
|
|
} else {
|
4636 |
|
|
$w tag remove var $i "insert-1c wordend"
|
4637 |
|
|
}
|
4638 |
|
|
}
|
4639 |
|
|
|
4640 |
|
|
## ::tkcon::MatchPair - blinks a matching pair of characters
|
4641 |
|
|
## c2 is assumed to be at the text index 'insert'.
|
4642 |
|
|
## This proc is really loopy and took me an hour to figure out given
|
4643 |
|
|
## all possible combinations with escaping except for escaped \'s.
|
4644 |
|
|
## It doesn't take into account possible commenting... Oh well. If
|
4645 |
|
|
## anyone has something better, I'd like to see/use it. This is really
|
4646 |
|
|
## only efficient for small contexts.
|
4647 |
|
|
# ARGS: w - console text widget
|
4648 |
|
|
# c1 - first char of pair
|
4649 |
|
|
# c2 - second char of pair
|
4650 |
|
|
# Calls: ::tkcon::Blink
|
4651 |
|
|
##
|
4652 |
|
|
proc ::tkcon::MatchPair {w c1 c2 {lim 1.0}} {
|
4653 |
|
|
if {[string compare {} [set ix [$w search -back $c1 insert $lim]]]} {
|
4654 |
|
|
while {
|
4655 |
|
|
[string match {\\} [$w get $ix-1c]] &&
|
4656 |
|
|
[string compare {} [set ix [$w search -back $c1 $ix-1c $lim]]]
|
4657 |
|
|
} {}
|
4658 |
|
|
set i1 insert-1c
|
4659 |
|
|
while {[string compare {} $ix]} {
|
4660 |
|
|
set i0 $ix
|
4661 |
|
|
set j 0
|
4662 |
|
|
while {[string compare {} [set i0 [$w search $c2 $i0 $i1]]]} {
|
4663 |
|
|
append i0 +1c
|
4664 |
|
|
if {[string match {\\} [$w get $i0-2c]]} continue
|
4665 |
|
|
incr j
|
4666 |
|
|
}
|
4667 |
|
|
if {!$j} break
|
4668 |
|
|
set i1 $ix
|
4669 |
|
|
while {$j && [string compare {} \
|
4670 |
|
|
[set ix [$w search -back $c1 $ix $lim]]]} {
|
4671 |
|
|
if {[string match {\\} [$w get $ix-1c]]} continue
|
4672 |
|
|
incr j -1
|
4673 |
|
|
}
|
4674 |
|
|
}
|
4675 |
|
|
if {[string match {} $ix]} { set ix [$w index $lim] }
|
4676 |
|
|
} else { set ix [$w index $lim] }
|
4677 |
|
|
if {$::tkcon::OPT(blinkrange)} {
|
4678 |
|
|
Blink $w $ix [$w index insert]
|
4679 |
|
|
} else {
|
4680 |
|
|
Blink $w $ix $ix+1c [$w index insert-1c] [$w index insert]
|
4681 |
|
|
}
|
4682 |
|
|
}
|
4683 |
|
|
|
4684 |
|
|
## ::tkcon::MatchQuote - blinks between matching quotes.
|
4685 |
|
|
## Blinks just the quote if it's unmatched, otherwise blinks quoted string
|
4686 |
|
|
## The quote to match is assumed to be at the text index 'insert'.
|
4687 |
|
|
# ARGS: w - console text widget
|
4688 |
|
|
# Calls: ::tkcon::Blink
|
4689 |
|
|
##
|
4690 |
|
|
proc ::tkcon::MatchQuote {w {lim 1.0}} {
|
4691 |
|
|
set i insert-1c
|
4692 |
|
|
set j 0
|
4693 |
|
|
while {[string compare [set i [$w search -back \" $i $lim]] {}]} {
|
4694 |
|
|
if {[string match {\\} [$w get $i-1c]]} continue
|
4695 |
|
|
if {!$j} {set i0 $i}
|
4696 |
|
|
incr j
|
4697 |
|
|
}
|
4698 |
|
|
if {$j&1} {
|
4699 |
|
|
if {$::tkcon::OPT(blinkrange)} {
|
4700 |
|
|
Blink $w $i0 [$w index insert]
|
4701 |
|
|
} else {
|
4702 |
|
|
Blink $w $i0 $i0+1c [$w index insert-1c] [$w index insert]
|
4703 |
|
|
}
|
4704 |
|
|
} else {
|
4705 |
|
|
Blink $w [$w index insert-1c] [$w index insert]
|
4706 |
|
|
}
|
4707 |
|
|
}
|
4708 |
|
|
|
4709 |
|
|
## ::tkcon::Blink - blinks between n index pairs for a specified duration.
|
4710 |
|
|
# ARGS: w - console text widget
|
4711 |
|
|
# i1 - start index to blink region
|
4712 |
|
|
# i2 - end index of blink region
|
4713 |
|
|
# dur - duration in usecs to blink for
|
4714 |
|
|
# Outputs: blinks selected characters in $w
|
4715 |
|
|
##
|
4716 |
|
|
proc ::tkcon::Blink {w args} {
|
4717 |
|
|
eval [list $w tag add blink] $args
|
4718 |
|
|
after $::tkcon::OPT(blinktime) [list $w] tag remove blink $args
|
4719 |
|
|
return
|
4720 |
|
|
}
|
4721 |
|
|
|
4722 |
|
|
|
4723 |
|
|
## ::tkcon::Insert
|
4724 |
|
|
## Insert a string into a text console at the point of the insertion cursor.
|
4725 |
|
|
## If there is a selection in the text, and it covers the point of the
|
4726 |
|
|
## insertion cursor, then delete the selection before inserting.
|
4727 |
|
|
# ARGS: w - text window in which to insert the string
|
4728 |
|
|
# s - string to insert (usually just a single char)
|
4729 |
|
|
# Outputs: $s to text widget
|
4730 |
|
|
##
|
4731 |
|
|
proc ::tkcon::Insert {w s} {
|
4732 |
|
|
if {[string match {} $s] || [string match disabled [$w cget -state]]} {
|
4733 |
|
|
return
|
4734 |
|
|
}
|
4735 |
|
|
if {[$w comp insert < limit]} {
|
4736 |
|
|
$w mark set insert end
|
4737 |
|
|
}
|
4738 |
|
|
if {[llength [$w tag ranges sel]] && \
|
4739 |
|
|
[$w comp sel.first <= insert] && [$w comp sel.last >= insert]} {
|
4740 |
|
|
$w delete sel.first sel.last
|
4741 |
|
|
}
|
4742 |
|
|
$w insert insert $s
|
4743 |
|
|
$w see insert
|
4744 |
|
|
}
|
4745 |
|
|
|
4746 |
|
|
## ::tkcon::Expand -
|
4747 |
|
|
# ARGS: w - text widget in which to expand str
|
4748 |
|
|
# type - type of expansion (path / proc / variable)
|
4749 |
|
|
# Calls: ::tkcon::Expand(Pathname|Procname|Variable)
|
4750 |
|
|
# Outputs: The string to match is expanded to the longest possible match.
|
4751 |
|
|
# If ::tkcon::OPT(showmultiple) is non-zero and the user longest
|
4752 |
|
|
# match equaled the string to expand, then all possible matches
|
4753 |
|
|
# are output to stdout. Triggers bell if no matches are found.
|
4754 |
|
|
# Returns: number of matches found
|
4755 |
|
|
##
|
4756 |
|
|
proc ::tkcon::Expand {w {type ""}} {
|
4757 |
|
|
set exp "\[^\\\\\]\[\[ \t\n\r\\\{\"$\]"
|
4758 |
|
|
set tmp [$w search -backwards -regexp $exp insert-1c limit-1c]
|
4759 |
|
|
if {[string compare {} $tmp]} {append tmp +2c} else {set tmp limit}
|
4760 |
|
|
if {[$w compare $tmp >= insert]} return
|
4761 |
|
|
set str [$w get $tmp insert]
|
4762 |
|
|
switch -glob $type {
|
4763 |
|
|
pa* { set res [ExpandPathname $str] }
|
4764 |
|
|
pr* { set res [ExpandProcname $str] }
|
4765 |
|
|
v* { set res [ExpandVariable $str] }
|
4766 |
|
|
default {
|
4767 |
|
|
set res {}
|
4768 |
|
|
foreach t $::tkcon::OPT(expandorder) {
|
4769 |
|
|
if {![catch {Expand$t $str} res] && \
|
4770 |
|
|
[string compare {} $res]} break
|
4771 |
|
|
}
|
4772 |
|
|
}
|
4773 |
|
|
}
|
4774 |
|
|
set len [llength $res]
|
4775 |
|
|
if {$len} {
|
4776 |
|
|
$w delete $tmp insert
|
4777 |
|
|
$w insert $tmp [lindex $res 0]
|
4778 |
|
|
if {$len > 1} {
|
4779 |
|
|
if {$::tkcon::OPT(showmultiple) && \
|
4780 |
|
|
![string compare [lindex $res 0] $str]} {
|
4781 |
|
|
puts stdout [lsort [lreplace $res 0 0]]
|
4782 |
|
|
}
|
4783 |
|
|
}
|
4784 |
|
|
} else { bell }
|
4785 |
|
|
return [incr len -1]
|
4786 |
|
|
}
|
4787 |
|
|
|
4788 |
|
|
## ::tkcon::ExpandPathname - expand a file pathname based on $str
|
4789 |
|
|
## This is based on UNIX file name conventions
|
4790 |
|
|
# ARGS: str - partial file pathname to expand
|
4791 |
|
|
# Calls: ::tkcon::ExpandBestMatch
|
4792 |
|
|
# Returns: list containing longest unique match followed by all the
|
4793 |
|
|
# possible further matches
|
4794 |
|
|
##
|
4795 |
|
|
proc ::tkcon::ExpandPathname str {
|
4796 |
|
|
set pwd [EvalAttached pwd]
|
4797 |
|
|
# Cause a string like {C:/Program\ Files/} to become "C:/Program Files/"
|
4798 |
|
|
regsub -all {\\([][ ])} $str {\1} str
|
4799 |
|
|
if {[catch {EvalAttached [list cd [file dirname $str]]} err]} {
|
4800 |
|
|
return -code error $err
|
4801 |
|
|
}
|
4802 |
|
|
set dir [file tail $str]
|
4803 |
|
|
## Check to see if it was known to be a directory and keep the trailing
|
4804 |
|
|
## slash if so (file tail cuts it off)
|
4805 |
|
|
if {[string match */ $str]} { append dir / }
|
4806 |
|
|
# Create a safely glob-able name
|
4807 |
|
|
regsub -all {([][])} $dir {\\\1} safedir
|
4808 |
|
|
if {[catch {lsort [EvalAttached [list glob $safedir*]]} m]} {
|
4809 |
|
|
set match {}
|
4810 |
|
|
} else {
|
4811 |
|
|
if {[llength $m] > 1} {
|
4812 |
|
|
global tcl_platform
|
4813 |
|
|
if {[string match windows $tcl_platform(platform)]} {
|
4814 |
|
|
## Windows is screwy because it's case insensitive
|
4815 |
|
|
set tmp [ExpandBestMatch [string tolower $m] \
|
4816 |
|
|
[string tolower $dir]]
|
4817 |
|
|
## Don't change case if we haven't changed the word
|
4818 |
|
|
if {[string length $dir]==[string length $tmp]} {
|
4819 |
|
|
set tmp $dir
|
4820 |
|
|
}
|
4821 |
|
|
} else {
|
4822 |
|
|
set tmp [ExpandBestMatch $m $dir]
|
4823 |
|
|
}
|
4824 |
|
|
if {[string match */* $str]} {
|
4825 |
|
|
set tmp [string trimright [file dirname $str] /]/$tmp
|
4826 |
|
|
}
|
4827 |
|
|
regsub -all {([^\\])([][ ])} $tmp {\1\\\2} tmp
|
4828 |
|
|
set match [linsert $m 0 $tmp]
|
4829 |
|
|
} else {
|
4830 |
|
|
## This may look goofy, but it handles spaces in path names
|
4831 |
|
|
eval append match $m
|
4832 |
|
|
if {[file isdirectory $match]} {append match /}
|
4833 |
|
|
if {[string match */* $str]} {
|
4834 |
|
|
set match [string trimright [file dirname $str] /]/$match
|
4835 |
|
|
}
|
4836 |
|
|
regsub -all {([^\\])([][ ])} $match {\1\\\2} match
|
4837 |
|
|
## Why is this one needed and the ones below aren't!!
|
4838 |
|
|
set match [list $match]
|
4839 |
|
|
}
|
4840 |
|
|
}
|
4841 |
|
|
EvalAttached [list cd $pwd]
|
4842 |
|
|
return $match
|
4843 |
|
|
}
|
4844 |
|
|
|
4845 |
|
|
## ::tkcon::ExpandProcname - expand a tcl proc name based on $str
|
4846 |
|
|
# ARGS: str - partial proc name to expand
|
4847 |
|
|
# Calls: ::tkcon::ExpandBestMatch
|
4848 |
|
|
# Returns: list containing longest unique match followed by all the
|
4849 |
|
|
# possible further matches
|
4850 |
|
|
##
|
4851 |
|
|
proc ::tkcon::ExpandProcname str {
|
4852 |
|
|
set match [EvalAttached [list info commands $str*]]
|
4853 |
|
|
if {[llength $match] == 0} {
|
4854 |
|
|
set ns [EvalAttached \
|
4855 |
|
|
"namespace children \[namespace current\] [list $str*]"]
|
4856 |
|
|
if {[llength $ns]==1} {
|
4857 |
|
|
set match [EvalAttached [list info commands ${ns}::*]]
|
4858 |
|
|
} else {
|
4859 |
|
|
set match $ns
|
4860 |
|
|
}
|
4861 |
|
|
}
|
4862 |
|
|
if {[llength $match] > 1} {
|
4863 |
|
|
regsub -all {([^\\]) } [ExpandBestMatch $match $str] {\1\\ } str
|
4864 |
|
|
set match [linsert $match 0 $str]
|
4865 |
|
|
} else {
|
4866 |
|
|
regsub -all {([^\\]) } $match {\1\\ } match
|
4867 |
|
|
}
|
4868 |
|
|
return $match
|
4869 |
|
|
}
|
4870 |
|
|
|
4871 |
|
|
## ::tkcon::ExpandVariable - expand a tcl variable name based on $str
|
4872 |
|
|
# ARGS: str - partial tcl var name to expand
|
4873 |
|
|
# Calls: ::tkcon::ExpandBestMatch
|
4874 |
|
|
# Returns: list containing longest unique match followed by all the
|
4875 |
|
|
# possible further matches
|
4876 |
|
|
##
|
4877 |
|
|
proc ::tkcon::ExpandVariable str {
|
4878 |
|
|
if {[regexp {([^\(]*)\((.*)} $str junk ary str]} {
|
4879 |
|
|
## Looks like they're trying to expand an array.
|
4880 |
|
|
set match [EvalAttached [list array names $ary $str*]]
|
4881 |
|
|
if {[llength $match] > 1} {
|
4882 |
|
|
set vars $ary\([ExpandBestMatch $match $str]
|
4883 |
|
|
foreach var $match {lappend vars $ary\($var\)}
|
4884 |
|
|
return $vars
|
4885 |
|
|
} else {set match $ary\($match\)}
|
4886 |
|
|
## Space transformation avoided for array names.
|
4887 |
|
|
} else {
|
4888 |
|
|
set match [EvalAttached [list info vars $str*]]
|
4889 |
|
|
if {[llength $match] > 1} {
|
4890 |
|
|
regsub -all {([^\\]) } [ExpandBestMatch $match $str] {\1\\ } str
|
4891 |
|
|
set match [linsert $match 0 $str]
|
4892 |
|
|
} else {
|
4893 |
|
|
regsub -all {([^\\]) } $match {\1\\ } match
|
4894 |
|
|
}
|
4895 |
|
|
}
|
4896 |
|
|
return $match
|
4897 |
|
|
}
|
4898 |
|
|
|
4899 |
|
|
## ::tkcon::ExpandBestMatch2 - finds the best unique match in a list of names
|
4900 |
|
|
## Improves upon the speed of the below proc only when $l is small
|
4901 |
|
|
## or $e is {}. $e is extra for compatibility with proc below.
|
4902 |
|
|
# ARGS: l - list to find best unique match in
|
4903 |
|
|
# Returns: longest unique match in the list
|
4904 |
|
|
##
|
4905 |
|
|
proc ::tkcon::ExpandBestMatch2 {l {e {}}} {
|
4906 |
|
|
set s [lindex $l 0]
|
4907 |
|
|
if {[llength $l]>1} {
|
4908 |
|
|
set i [expr {[string length $s]-1}]
|
4909 |
|
|
foreach l $l {
|
4910 |
|
|
while {$i>=0 && [string first $s $l]} {
|
4911 |
|
|
set s [string range $s 0 [incr i -1]]
|
4912 |
|
|
}
|
4913 |
|
|
}
|
4914 |
|
|
}
|
4915 |
|
|
return $s
|
4916 |
|
|
}
|
4917 |
|
|
|
4918 |
|
|
## ::tkcon::ExpandBestMatch - finds the best unique match in a list of names
|
4919 |
|
|
## The extra $e in this argument allows us to limit the innermost loop a
|
4920 |
|
|
## little further. This improves speed as $l becomes large or $e becomes long.
|
4921 |
|
|
# ARGS: l - list to find best unique match in
|
4922 |
|
|
# e - currently best known unique match
|
4923 |
|
|
# Returns: longest unique match in the list
|
4924 |
|
|
##
|
4925 |
|
|
proc ::tkcon::ExpandBestMatch {l {e {}}} {
|
4926 |
|
|
set ec [lindex $l 0]
|
4927 |
|
|
if {[llength $l]>1} {
|
4928 |
|
|
set e [string length $e]; incr e -1
|
4929 |
|
|
set ei [string length $ec]; incr ei -1
|
4930 |
|
|
foreach l $l {
|
4931 |
|
|
while {$ei>=$e && [string first $ec $l]} {
|
4932 |
|
|
set ec [string range $ec 0 [incr ei -1]]
|
4933 |
|
|
}
|
4934 |
|
|
}
|
4935 |
|
|
}
|
4936 |
|
|
return $ec
|
4937 |
|
|
}
|
4938 |
|
|
|
4939 |
|
|
# Here is a group of functions that is only used when Tkcon is
|
4940 |
|
|
# executed in a safe interpreter. It provides safe versions of
|
4941 |
|
|
# missing functions. For example:
|
4942 |
|
|
#
|
4943 |
|
|
# - "tk appname" returns "tkcon.tcl" but cannot be set
|
4944 |
|
|
# - "toplevel" is equivalent to 'frame', only it is automatically
|
4945 |
|
|
# packed.
|
4946 |
|
|
# - The 'source', 'load', 'open', 'file' and 'exit' functions are
|
4947 |
|
|
# mapped to corresponding functions in the parent interpreter.
|
4948 |
|
|
#
|
4949 |
|
|
# Further on, Tk cannot be really loaded. Still the safe 'load'
|
4950 |
|
|
# provedes a speciall case. The Tk can be divided into 4 groups,
|
4951 |
|
|
# that each has a safe handling procedure.
|
4952 |
|
|
#
|
4953 |
|
|
# - "::tkcon::SafeItem" handles commands like 'button', 'canvas' ......
|
4954 |
|
|
# Each of these functions has the window name as first argument.
|
4955 |
|
|
# - "::tkcon::SafeManage" handles commands like 'pack', 'place', 'grid',
|
4956 |
|
|
# 'winfo', which can have multiple window names as arguments.
|
4957 |
|
|
# - "::tkcon::SafeWindow" handles all windows, such as '.'. For every
|
4958 |
|
|
# window created, a new alias is formed which also is handled by
|
4959 |
|
|
# this function.
|
4960 |
|
|
# - Other (e.g. bind, bindtag, image), which need their own function.
|
4961 |
|
|
#
|
4962 |
|
|
## These functions courtesy Jan Nijtmans (nijtmans@nici.kun.nl)
|
4963 |
|
|
##
|
4964 |
|
|
if {[string compare [info command tk] tk]} {
|
4965 |
|
|
proc tk {option args} {
|
4966 |
|
|
if {![string match app* $option]} {
|
4967 |
|
|
error "wrong option \"$option\": should be appname"
|
4968 |
|
|
}
|
4969 |
|
|
return "tkcon.tcl"
|
4970 |
|
|
}
|
4971 |
|
|
}
|
4972 |
|
|
|
4973 |
|
|
if {[string compare [info command toplevel] toplevel]} {
|
4974 |
|
|
proc toplevel {name args} {
|
4975 |
|
|
eval frame $name $args
|
4976 |
|
|
pack $name
|
4977 |
|
|
}
|
4978 |
|
|
}
|
4979 |
|
|
|
4980 |
|
|
proc ::tkcon::SafeSource {i f} {
|
4981 |
|
|
set fd [open $f r]
|
4982 |
|
|
set r [read $fd]
|
4983 |
|
|
close $fd
|
4984 |
|
|
if {[catch {interp eval $i $r} msg]} {
|
4985 |
|
|
error $msg
|
4986 |
|
|
}
|
4987 |
|
|
}
|
4988 |
|
|
|
4989 |
|
|
proc ::tkcon::SafeOpen {i f {m r}} {
|
4990 |
|
|
set fd [open $f $m]
|
4991 |
|
|
interp transfer {} $fd $i
|
4992 |
|
|
return $fd
|
4993 |
|
|
}
|
4994 |
|
|
|
4995 |
|
|
proc ::tkcon::SafeLoad {i f p} {
|
4996 |
|
|
global tk_version tk_patchLevel tk_library auto_path
|
4997 |
|
|
if {[string compare $p Tk]} {
|
4998 |
|
|
load $f $p $i
|
4999 |
|
|
} else {
|
5000 |
|
|
foreach command {button canvas checkbutton entry frame label
|
5001 |
|
|
listbox message radiobutton scale scrollbar spinbox text toplevel} {
|
5002 |
|
|
$i alias $command ::tkcon::SafeItem $i $command
|
5003 |
|
|
}
|
5004 |
|
|
$i alias image ::tkcon::SafeImage $i
|
5005 |
|
|
foreach command {pack place grid destroy winfo} {
|
5006 |
|
|
$i alias $command ::tkcon::SafeManage $i $command
|
5007 |
|
|
}
|
5008 |
|
|
if {[llength [info command event]]} {
|
5009 |
|
|
$i alias event ::tkcon::SafeManage $i $command
|
5010 |
|
|
}
|
5011 |
|
|
frame .${i}_dot -width 300 -height 300 -relief raised
|
5012 |
|
|
pack .${i}_dot -side left
|
5013 |
|
|
$i alias tk tk
|
5014 |
|
|
$i alias bind ::tkcon::SafeBind $i
|
5015 |
|
|
$i alias bindtags ::tkcon::SafeBindtags $i
|
5016 |
|
|
$i alias . ::tkcon::SafeWindow $i {}
|
5017 |
|
|
foreach var {tk_version tk_patchLevel tk_library auto_path} {
|
5018 |
|
|
$i eval set $var [list [set $var]]
|
5019 |
|
|
}
|
5020 |
|
|
$i eval {
|
5021 |
|
|
package provide Tk $tk_version
|
5022 |
|
|
if {[lsearch -exact $auto_path $tk_library] < 0} {
|
5023 |
|
|
lappend auto_path $tk_library
|
5024 |
|
|
}
|
5025 |
|
|
}
|
5026 |
|
|
return ""
|
5027 |
|
|
}
|
5028 |
|
|
}
|
5029 |
|
|
|
5030 |
|
|
proc ::tkcon::SafeSubst {i a} {
|
5031 |
|
|
set arg1 ""
|
5032 |
|
|
foreach {arg value} $a {
|
5033 |
|
|
if {![string compare $arg -textvariable] ||
|
5034 |
|
|
![string compare $arg -variable]} {
|
5035 |
|
|
set newvalue "[list $i] $value"
|
5036 |
|
|
global $newvalue
|
5037 |
|
|
if {[interp eval $i info exists $value]} {
|
5038 |
|
|
set $newvalue [interp eval $i set $value]
|
5039 |
|
|
} else {
|
5040 |
|
|
catch {unset $newvalue}
|
5041 |
|
|
}
|
5042 |
|
|
$i eval trace variable $value rwu \{[list tkcon set $newvalue $i]\}
|
5043 |
|
|
set value $newvalue
|
5044 |
|
|
} elseif {![string compare $arg -command]} {
|
5045 |
|
|
set value [list $i eval $value]
|
5046 |
|
|
}
|
5047 |
|
|
lappend arg1 $arg $value
|
5048 |
|
|
}
|
5049 |
|
|
return $arg1
|
5050 |
|
|
}
|
5051 |
|
|
|
5052 |
|
|
proc ::tkcon::SafeItem {i command w args} {
|
5053 |
|
|
set args [::tkcon::SafeSubst $i $args]
|
5054 |
|
|
set code [catch "$command [list .${i}_dot$w] $args" msg]
|
5055 |
|
|
$i alias $w ::tkcon::SafeWindow $i $w
|
5056 |
|
|
regsub -all .${i}_dot $msg {} msg
|
5057 |
|
|
return -code $code $msg
|
5058 |
|
|
}
|
5059 |
|
|
|
5060 |
|
|
proc ::tkcon::SafeManage {i command args} {
|
5061 |
|
|
set args1 ""
|
5062 |
|
|
foreach arg $args {
|
5063 |
|
|
if {[string match . $arg]} {
|
5064 |
|
|
set arg .${i}_dot
|
5065 |
|
|
} elseif {[string match .* $arg]} {
|
5066 |
|
|
set arg ".${i}_dot$arg"
|
5067 |
|
|
}
|
5068 |
|
|
lappend args1 $arg
|
5069 |
|
|
}
|
5070 |
|
|
set code [catch "$command $args1" msg]
|
5071 |
|
|
regsub -all .${i}_dot $msg {} msg
|
5072 |
|
|
return -code $code $msg
|
5073 |
|
|
}
|
5074 |
|
|
|
5075 |
|
|
#
|
5076 |
|
|
# FIX: this function doesn't work yet if the binding starts with '+'.
|
5077 |
|
|
#
|
5078 |
|
|
proc ::tkcon::SafeBind {i w args} {
|
5079 |
|
|
if {[string match . $w]} {
|
5080 |
|
|
set w .${i}_dot
|
5081 |
|
|
} elseif {[string match .* $w]} {
|
5082 |
|
|
set w ".${i}_dot$w"
|
5083 |
|
|
}
|
5084 |
|
|
if {[llength $args] > 1} {
|
5085 |
|
|
set args [list [lindex $args 0] \
|
5086 |
|
|
"[list $i] eval [list [lindex $args 1]]"]
|
5087 |
|
|
}
|
5088 |
|
|
set code [catch "bind $w $args" msg]
|
5089 |
|
|
if {[llength $args] <2 && $code == 0} {
|
5090 |
|
|
set msg [lindex $msg 3]
|
5091 |
|
|
}
|
5092 |
|
|
return -code $code $msg
|
5093 |
|
|
}
|
5094 |
|
|
|
5095 |
|
|
proc ::tkcon::SafeImage {i option args} {
|
5096 |
|
|
set code [catch "image $option $args" msg]
|
5097 |
|
|
if {[string match cr* $option]} {
|
5098 |
|
|
$i alias $msg $msg
|
5099 |
|
|
}
|
5100 |
|
|
return -code $code $msg
|
5101 |
|
|
}
|
5102 |
|
|
|
5103 |
|
|
proc ::tkcon::SafeBindtags {i w {tags {}}} {
|
5104 |
|
|
if {[string match . $w]} {
|
5105 |
|
|
set w .${i}_dot
|
5106 |
|
|
} elseif {[string match .* $w]} {
|
5107 |
|
|
set w ".${i}_dot$w"
|
5108 |
|
|
}
|
5109 |
|
|
set newtags {}
|
5110 |
|
|
foreach tag $tags {
|
5111 |
|
|
if {[string match . $tag]} {
|
5112 |
|
|
lappend newtags .${i}_dot
|
5113 |
|
|
} elseif {[string match .* $tag]} {
|
5114 |
|
|
lappend newtags ".${i}_dot$tag"
|
5115 |
|
|
} else {
|
5116 |
|
|
lappend newtags $tag
|
5117 |
|
|
}
|
5118 |
|
|
}
|
5119 |
|
|
if {[string match $tags {}]} {
|
5120 |
|
|
set code [catch {bindtags $w} msg]
|
5121 |
|
|
regsub -all \\.${i}_dot $msg {} msg
|
5122 |
|
|
} else {
|
5123 |
|
|
set code [catch {bindtags $w $newtags} msg]
|
5124 |
|
|
}
|
5125 |
|
|
return -code $code $msg
|
5126 |
|
|
}
|
5127 |
|
|
|
5128 |
|
|
proc ::tkcon::SafeWindow {i w option args} {
|
5129 |
|
|
if {[string match conf* $option] && [llength $args] > 1} {
|
5130 |
|
|
set args [::tkcon::SafeSubst $i $args]
|
5131 |
|
|
} elseif {[string match itemco* $option] && [llength $args] > 2} {
|
5132 |
|
|
set args "[list [lindex $args 0]] [::tkcon::SafeSubst $i [lrange $args 1 end]]"
|
5133 |
|
|
} elseif {[string match cr* $option]} {
|
5134 |
|
|
if {[llength $args]%2} {
|
5135 |
|
|
set args "[list [lindex $args 0]] [::tkcon::SafeSubst $i [lrange $args 1 end]]"
|
5136 |
|
|
} else {
|
5137 |
|
|
set args [::tkcon::SafeSubst $i $args]
|
5138 |
|
|
}
|
5139 |
|
|
} elseif {[string match bi* $option] && [llength $args] > 2} {
|
5140 |
|
|
set args [list [lindex $args 0] [lindex $args 1] "[list $i] eval [list [lindex $args 2]]"]
|
5141 |
|
|
}
|
5142 |
|
|
set code [catch ".${i}_dot$w $option $args" msg]
|
5143 |
|
|
if {$code} {
|
5144 |
|
|
regsub -all .${i}_dot $msg {} msg
|
5145 |
|
|
} elseif {[string match conf* $option] || [string match itemco* $option]} {
|
5146 |
|
|
if {[llength $args] == 1} {
|
5147 |
|
|
switch -- $args {
|
5148 |
|
|
-textvariable - -variable {
|
5149 |
|
|
set msg "[lrange $msg 0 3] [list [lrange [lindex $msg 4] 1 end]]"
|
5150 |
|
|
}
|
5151 |
|
|
-command - updatecommand {
|
5152 |
|
|
set msg "[lrange $msg 0 3] [list [lindex [lindex $msg 4] 2]]"
|
5153 |
|
|
}
|
5154 |
|
|
}
|
5155 |
|
|
} elseif {[llength $args] == 0} {
|
5156 |
|
|
set args1 ""
|
5157 |
|
|
foreach el $msg {
|
5158 |
|
|
switch -- [lindex $el 0] {
|
5159 |
|
|
-textvariable - -variable {
|
5160 |
|
|
set el "[lrange $el 0 3] [list [lrange [lindex $el 4] 1 end]]"
|
5161 |
|
|
}
|
5162 |
|
|
-command - updatecommand {
|
5163 |
|
|
set el "[lrange $el 0 3] [list [lindex [lindex $el 4] 2]]"
|
5164 |
|
|
}
|
5165 |
|
|
}
|
5166 |
|
|
lappend args1 $el
|
5167 |
|
|
}
|
5168 |
|
|
set msg $args1
|
5169 |
|
|
}
|
5170 |
|
|
} elseif {[string match cg* $option] || [string match itemcg* $option]} {
|
5171 |
|
|
switch -- $args {
|
5172 |
|
|
-textvariable - -variable {
|
5173 |
|
|
set msg [lrange $msg 1 end]
|
5174 |
|
|
}
|
5175 |
|
|
-command - updatecommand {
|
5176 |
|
|
set msg [lindex $msg 2]
|
5177 |
|
|
}
|
5178 |
|
|
}
|
5179 |
|
|
} elseif {[string match bi* $option]} {
|
5180 |
|
|
if {[llength $args] == 2 && $code == 0} {
|
5181 |
|
|
set msg [lindex $msg 2]
|
5182 |
|
|
}
|
5183 |
|
|
}
|
5184 |
|
|
return -code $code $msg
|
5185 |
|
|
}
|
5186 |
|
|
|
5187 |
|
|
proc ::tkcon::RetrieveFilter {host} {
|
5188 |
|
|
variable PRIV
|
5189 |
|
|
set result {}
|
5190 |
|
|
if {[info exists PRIV(proxy)]} {
|
5191 |
|
|
if {![regexp "^(localhost|127\.0\.0\.1)" $host]} {
|
5192 |
|
|
set result [lrange [split [lindex $PRIV(proxy) 0] :] 0 1]
|
5193 |
|
|
}
|
5194 |
|
|
}
|
5195 |
|
|
return $result
|
5196 |
|
|
}
|
5197 |
|
|
|
5198 |
|
|
proc ::tkcon::RetrieveAuthentication {} {
|
5199 |
|
|
package require Tk
|
5200 |
|
|
if {[catch {package require base64}]} {
|
5201 |
|
|
if {[catch {package require Trf}]} {
|
5202 |
|
|
error "base64 support not available"
|
5203 |
|
|
} else {
|
5204 |
|
|
set local64 "base64 -mode enc"
|
5205 |
|
|
}
|
5206 |
|
|
} else {
|
5207 |
|
|
set local64 "base64::encode"
|
5208 |
|
|
}
|
5209 |
|
|
|
5210 |
|
|
set dlg [toplevel .auth]
|
5211 |
|
|
wm title $dlg "Authenticating Proxy Configuration"
|
5212 |
|
|
set f1 [frame ${dlg}.f1]
|
5213 |
|
|
set f2 [frame ${dlg}.f2]
|
5214 |
|
|
button $f2.b -text "OK" -command "destroy $dlg"
|
5215 |
|
|
pack $f2.b -side right
|
5216 |
|
|
label $f1.l2 -text "Username"
|
5217 |
|
|
label $f1.l3 -text "Password"
|
5218 |
|
|
entry $f1.e2 -textvariable "[namespace current]::conf_userid"
|
5219 |
|
|
entry $f1.e3 -textvariable "[namespace current]::conf_passwd" -show *
|
5220 |
|
|
grid $f1.l2 -column 0 -row 0 -sticky e
|
5221 |
|
|
grid $f1.l3 -column 0 -row 1 -sticky e
|
5222 |
|
|
grid $f1.e2 -column 1 -row 0 -sticky news
|
5223 |
|
|
grid $f1.e3 -column 1 -row 1 -sticky news
|
5224 |
|
|
grid columnconfigure $f1 1 -weight 1
|
5225 |
|
|
pack $f2 -side bottom -fill x
|
5226 |
|
|
pack $f1 -side top -anchor n -fill both -expand 1
|
5227 |
|
|
tkwait window $dlg
|
5228 |
|
|
set result {}
|
5229 |
|
|
if {[info exists [namespace current]::conf_userid]} {
|
5230 |
|
|
set data [subst $[namespace current]::conf_userid]
|
5231 |
|
|
append data : [subst $[namespace current]::conf_passwd]
|
5232 |
|
|
set data [$local64 $data]
|
5233 |
|
|
set result [list "Proxy-Authorization" "Basic $data"]
|
5234 |
|
|
}
|
5235 |
|
|
unset [namespace current]::conf_passwd
|
5236 |
|
|
return $result
|
5237 |
|
|
}
|
5238 |
|
|
|
5239 |
|
|
proc ::tkcon::Retrieve {} {
|
5240 |
|
|
# A little bit'o'magic to grab the latest tkcon from CVS and
|
5241 |
|
|
# save it locally. It doesn't support proxies though...
|
5242 |
|
|
variable PRIV
|
5243 |
|
|
|
5244 |
|
|
set defExt ""
|
5245 |
|
|
if {[string match "windows" $::tcl_platform(platform)]} {
|
5246 |
|
|
set defExt ".tcl"
|
5247 |
|
|
}
|
5248 |
|
|
set file [tk_getSaveFile -title "Save Latest tkcon to ..." \
|
5249 |
|
|
-defaultextension $defExt \
|
5250 |
|
|
-initialdir [file dirname $PRIV(SCRIPT)] \
|
5251 |
|
|
-initialfile [file tail $PRIV(SCRIPT)] \
|
5252 |
|
|
-parent $PRIV(root) \
|
5253 |
|
|
-filetypes {{"Tcl Files" {.tcl .tk}} {"All Files" {*.*}}}]
|
5254 |
|
|
if {[string compare $file ""]} {
|
5255 |
|
|
package require http 2
|
5256 |
|
|
set token [::http::geturl $PRIV(HEADURL) -timeout 30000]
|
5257 |
|
|
::http::wait $token
|
5258 |
|
|
set code [catch {
|
5259 |
|
|
if {[::http::status $token] == "ok"} {
|
5260 |
|
|
set fid [open $file w]
|
5261 |
|
|
# We don't want newline mode to change
|
5262 |
|
|
fconfigure $fid -translation binary
|
5263 |
|
|
set data [::http::data $token]
|
5264 |
|
|
puts -nonewline $fid $data
|
5265 |
|
|
close $fid
|
5266 |
|
|
regexp {Id: tkcon.tcl,v (\d+\.\d+)} $data -> rcsVersion
|
5267 |
|
|
regexp {version\s+(\d+\.\d[^\n]*)} $data -> tkconVersion
|
5268 |
|
|
}
|
5269 |
|
|
} err]
|
5270 |
|
|
::http::cleanup $token
|
5271 |
|
|
if {$code} {
|
5272 |
|
|
return -code error $err
|
5273 |
|
|
} elseif {[tk_messageBox -type yesno -icon info -parent $PRIV(root) \
|
5274 |
|
|
-title "Retrieved tkcon v$tkconVersion, RCS $rcsVersion" \
|
5275 |
|
|
-message "Successfully retrieved tkcon v$tkconVersion,\
|
5276 |
|
|
RCS $rcsVersion. Shall I resource (not restart) this\
|
5277 |
|
|
version now?"] == "yes"} {
|
5278 |
|
|
set PRIV(SCRIPT) $file
|
5279 |
|
|
set PRIV(version) $tkconVersion.$rcsVersion
|
5280 |
|
|
::tkcon::Resource
|
5281 |
|
|
}
|
5282 |
|
|
}
|
5283 |
|
|
}
|
5284 |
|
|
|
5285 |
|
|
## ::tkcon::Resource - re'source's this script into current console
|
5286 |
|
|
## Meant primarily for my development of this program. It follows
|
5287 |
|
|
## links until the ultimate source is found.
|
5288 |
|
|
##
|
5289 |
|
|
set ::tkcon::PRIV(SCRIPT) [info script]
|
5290 |
|
|
if {!$::tkcon::PRIV(WWW) && [string compare $::tkcon::PRIV(SCRIPT) {}]} {
|
5291 |
|
|
# we use a catch here because some wrap apps choke on 'file type'
|
5292 |
|
|
# because TclpLstat wasn't wrappable until 8.4.
|
5293 |
|
|
catch {
|
5294 |
|
|
while {[string match link [file type $::tkcon::PRIV(SCRIPT)]]} {
|
5295 |
|
|
set link [file readlink $::tkcon::PRIV(SCRIPT)]
|
5296 |
|
|
if {[string match relative [file pathtype $link]]} {
|
5297 |
|
|
set ::tkcon::PRIV(SCRIPT) \
|
5298 |
|
|
[file join [file dirname $::tkcon::PRIV(SCRIPT)] $link]
|
5299 |
|
|
} else {
|
5300 |
|
|
set ::tkcon::PRIV(SCRIPT) $link
|
5301 |
|
|
}
|
5302 |
|
|
}
|
5303 |
|
|
catch {unset link}
|
5304 |
|
|
if {[string match relative [file pathtype $::tkcon::PRIV(SCRIPT)]]} {
|
5305 |
|
|
set ::tkcon::PRIV(SCRIPT) [file join [pwd] $::tkcon::PRIV(SCRIPT)]
|
5306 |
|
|
}
|
5307 |
|
|
}
|
5308 |
|
|
}
|
5309 |
|
|
|
5310 |
|
|
proc ::tkcon::Resource {} {
|
5311 |
|
|
uplevel \#0 {
|
5312 |
|
|
if {[catch {source -rsrc tkcon}]} { source $::tkcon::PRIV(SCRIPT) }
|
5313 |
|
|
}
|
5314 |
|
|
Bindings
|
5315 |
|
|
InitSlave $::tkcon::OPT(exec)
|
5316 |
|
|
}
|
5317 |
|
|
|
5318 |
|
|
## Initialize only if we haven't yet
|
5319 |
|
|
##
|
5320 |
|
|
if {![info exists ::tkcon::PRIV(root)] || \
|
5321 |
|
|
![winfo exists $::tkcon::PRIV(root)]} {
|
5322 |
|
|
::tkcon::Init
|
5323 |
|
|
}
|