OpenCores
URL https://opencores.org/ocsvn/pavr/pavr/trunk

Subversion Repositories pavr

[/] [pavr/] [trunk/] [tools/] [common/] [projman.tcl] - Blame information for rev 6

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.