home *** CD-ROM | disk | FTP | other *** search
/ PC World 2002 May / PCWorld_2002-05_cd.bin / Software / TemaCD / activetcltk / ActiveTcl8.3.4.1-8.win32-ix86.exe / ActiveTcl8.3.4.1-win32-ix86 / bin / tkcon.tcl < prev    next >
Encoding:
Text File  |  2001-10-22  |  150.1 KB  |  5,042 lines

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