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