| # util-dump.tcl -- |
| # |
| # This file implements package ::Utility::dump, which ... |
| # |
| # Copyright (c) 1997-8 Jeffrey Hobbs |
| # |
| # See the file "license.terms" for information on usage and |
| # redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. |
| # |
| |
| package require ::Utility |
| package provide ::Utility::dump 1.0 |
| |
| namespace eval ::Utility::dump {; |
| |
| namespace export -clear dump* |
| namespace import -force ::Utility::get_opts* |
| |
| # dump -- |
| # outputs recognized item info in source'able form. |
| # Accepts glob style pattern matching for the names |
| # Arguments: |
| # type type of item to dump |
| # -nocomplain |
| # -filter pattern |
| # specifies a glob filter pattern to be used by the variable |
| # method as an array filter pattern (it filters down for |
| # nested elements) and in the widget method as a config |
| # option filter pattern |
| # -procs |
| # -vars |
| # -recursive |
| # -imports |
| # -- forcibly ends options recognition |
| # Results: |
| # the values of the requested items in a 'source'able form |
| ;proc dump {type arg |
| s} { |
| if {![llength $args]} { |
| ## If no args, assume they gave us something to dump and |
| ## we'll try anything |
| set args [list $type] |
| set type multi |
| } |
| ## Args are handled individually by the routines because of the |
| ## variable parameters for each type |
| set prefix [namespace current]::dump_ |
| if {[string match {} [set arg [info commands $prefix$type]]]} { |
| set arg [info commands $prefix$type*] |
| } |
| set result {} |
| set code ok |
| switch [llength $arg] { |
| 1 { set code [catch {uplevel $arg $args} result] } |
| 0 { |
| set arg [info commands $prefix*] |
| regsub -all $prefix $arg {} arg |
| return -code error "unknown [lindex [info level 0] 0] type\ |
| \"$type\", must be one of: [join [lsort $arg] {, }]" |
| } |
| default { |
| regsub -all $prefix $arg {} arg |
| return -code error "ambiguous type \"$type\",\ |
| could be one of: [join [lsort $arg] {, }]" |
| } |
| } |
| return -code $code $result |
| } |
| |
| # dump_multi -- |
| # |
| # Tries to work the args into one of the main dump types: |
| # variable, command, widget, namespace |
| # |
| # Arguments: |
| # args comments |
| # Results: |
| # Returns ... |
| # |
| proc dump_multi {args} { |
| array set opts { |
| -nocomplain 0 |
| } |
| set namesp [namespace current] |
| set args [get_opts opts $args {-nocomplain 0} {} 1] |
| set code ok |
| if { |
| [catch {uplevel ${namesp}::dump var $args} err] && |
| [catch {uplevel ${namesp}::dump com $args} err] && |
| [catch {uplevel ${namesp}::dump wid $args} err] && |
| [catch {uplevel ${namesp}::dump nam $args} err] |
| } { |
| set result "# unable to resolve type for \"$args\"\n" |
| if {!$opts(-nocomplain)} { |
| set code error |
| } |
| } else { |
| set result $err |
| } |
| return -code $code [string trimright $result \n] |
| } |
| |
| # dump_command -- |
| # |
| # outputs commands by figuring out, as well as possible, |
| # it does not attempt to auto-load anything |
| # |
| # Arguments: |
| # args comments |
| # Results: |
| # Returns ... |
| # |
| proc dump_command {args} { |
| array set opts { |
| -nocomplain 0 -origin 0 |
| } |
| set args [get_opts opts $args {-nocomplain 0 -origin 0}] |
| if {[string match {} $args]} { |
| if {$opts(-nocomplain)} { |
| return |
| } else { |
| return -code error "wrong \# args: dump command ?-nocomplain?" |
| } |
| } |
| set code ok |
| set result {} |
| set namesp [namespace current] |
| foreach arg $args { |
| if {[string compare {} [set cmds \ |
| [uplevel info command [list $arg]]]]} { |
| foreach cmd [lsort $cmds] { |
| if {[lsearch -exact [interp aliases] $cmd] > -1} { |
| append result "\#\# ALIAS: $cmd =>\ |
| [interp alias {} $cmd]\n" |
| } elseif {![catch {uplevel ${namesp}::dump_proc \ |
| [expr {$opts(-origin)?{-origin}:{}}] \ |
| -- [list $cmd]} msg]} { |
| append result $msg\n |
| } else { |
| if {$opts(-origin) || [string compare $namesp \ |
| [uplevel namespace current]]} { |
| set cmd [uplevel namespace origin [list $cmd]] |
| } |
| append result "\#\# COMMAND: $cmd\n" |
| } |
| } |
| } elseif {!$opts(-nocomplain)} { |
| append result "\#\# No known command $arg\n" |
| set code error |
| } |
| } |
| return -code $code [string trimright $result \n] |
| } |
| |
| # dump_proc -- |
| # |
| # ADD COMMENTS HERE |
| # |
| # Arguments: |
| # args comments |
| # Results: |
| # Returns ... |
| # |
| proc dump_proc {args} { |
| array set opts { |
| -nocomplain 0 -origin 0 |
| } |
| set args [get_opts opts $args {-nocomplain 0 -origin 0}] |
| if {[string match {} $args]} { |
| if {$opts(-nocomplain)} { |
| return |
| } else { |
| return -code error "wrong \# args: dump proc ?-nocomplain?" |
| } |
| } |
| set code ok |
| set result {} |
| foreach arg $args { |
| set procs [uplevel info command [list $arg]] |
| set count 0 |
| if {[string compare $procs {}]} { |
| foreach p [lsort $procs] { |
| set cmd [uplevel namespace origin [list $p]] |
| set namesp [namespace qualifiers $cmd] |
| if {[string match {} $namesp]} { set namesp :: } |
| if {[string compare [namespace eval $namesp \ |
| info procs [list [namespace tail $cmd]]] {}]} { |
| incr count |
| } else { |
| continue |
| } |
| set pargs {} |
| foreach a [info args $cmd] { |
| if {[info default $cmd $a tmp]} { |
| lappend pargs [list $a $tmp] |
| } else { |
| lappend pargs $a |
| } |
| } |
| if {$opts(-origin) || [string compare $namesp \ |
| [uplevel namespace current]]} { |
| ## This is ideal, but list can really screw with the |
| ## format of the body for some procs with odd whitespacing |
| ## (everything comes out backslashed) |
| #append result [list proc $cmd $pargs [info body $cmd]] |
| append result [list proc $cmd $pargs] |
| } else { |
| ## We don't include the full namespace qualifiers |
| ## if we are in the namespace of origin |
| #append result [list proc $p $pargs [info body $cmd]] |
| append result [list proc $p $pargs] |
| } |
| append result " \{[info body $cmd]\}\n\n" |
| } |
| } |
| if {!$count && !$opts(-nocomplain)} { |
| append result "\#\# No known proc $arg\n" |
| set code error |
| } |
| } |
| return -code $code [string trimright $result \n] |
| } |
| |
| # dump_variable -- |
| # |
| # outputs variable value(s), whether array or simple, namespaced or otherwise |
| # |
| # Arguments: |
| # args comments |
| # Results: |
| # Returns ... |
| # |
| ## FIX perhaps a little namespace which is necessary here |
| proc dump_variable {args} { |
| array set opts { |
| -nocomplain 0 -filter * |
| } |
| set args [get_opts opts $args {-nocomplain 0 -filter 1}] |
| if {[string match {} $args]} { |
| if {$opts(-nocomplain)} { |
| return |
| } else { |
| return -code error "wrong \# args: dump variable ?-nocomplain?\ |
| ?-filter glob? ?--? pattern ?pattern ...?" |
| } |
| } |
| set code ok |
| set result {} |
| foreach arg $args { |
| if {[string match {} [set vars [uplevel info vars [list $arg]]]]} { |
| if {[uplevel info exists $arg]} { |
| set vars $arg |
| } elseif {!$opts(-nocomplain)} { |
| append result "\#\# No known variable $arg\n" |
| set code error |
| continue |
| } else { continue } |
| } |
| foreach var [lsort -dictionary $vars] { |
| set var [uplevel [list namespace which -variable $var]] |
| upvar $var v |
| if {[array exists v] || [catch {string length $v}]} { |
| set nest {} |
| append result "array set $var \{\n" |
| foreach i [lsort -dictionary [array names v $opts(-filter)]] { |
| upvar 0 v\($i\) __ary |
| if {[array exists __ary]} { |
| append nest "\#\# NESTED ARRAY ELEMENT: $i\n" |
| append nest "upvar 0 [list $var\($i\)] __ary;\ |
| [dump v -filter $opts(-filter) __ary]\n" |
| } else { |
| append result " [list $i]\t[list $v($i)]\n" |
| } |
| } |
| append result "\}\n$nest" |
| } else { |
| append result [list set $var $v]\n |
| } |
| } |
| } |
| return -code $code [string trimright $result \n] |
| } |
| |
| # dump_namespace -- |
| # |
| # ADD COMMENTS HERE |
| # |
| # Arguments: |
| # args comments |
| # Results: |
| # Returns ... |
| # |
| proc dump_namespace {args} { |
| array set opts { |
| -nocomplain 0 -filter * -procs 1 -vars 1 -recursive 0 -imports 1 |
| } |
| set args [get_opts opts $args {-nocomplain 0 -procs 1 -vars 1 \ |
| -recursive 0 -imports 1} {-procs boolean -vars boolean \ |
| -imports boolean}] |
| if {[string match {} $args]} { |
| if {$opts(-nocomplain)} { |
| return |
| } else { |
| return -code error "wrong \# args: dump namespace ?-nocomplain?\ |
| ?-procs 0/1? ?-vars 0/1? ?-recursive? ?-imports 0/1?\ |
| ?--? pattern ?pattern ...?" |
| } |
| } |
| set code ok |
| set result {} |
| foreach arg $args { |
| set cur [uplevel namespace current] |
| # Namespace search order: |
| # If it starts with ::, try and break it apart and see if we find |
| # children matching the pattern |
| # Then do the same in $cur if it has :: anywhere in it |
| # Then look in the calling namespace for children matching $arg |
| # Then look in the global namespace for children matching $arg |
| if { |
| ([string match ::* $arg] && |
| [catch [list namespace children [namespace qualifiers $arg] \ |
| [namespace tail $arg]] names]) && |
| ([string match *::* $arg] && |
| [catch [list namespace eval $cur [list namespace children \ |
| [namespace qualifiers $arg] \ |
| [namespace tail $arg]] names]]) && |
| [catch [list namespace children $cur $arg] names] && |
| [catch [list namespace children :: $arg] names] |
| } { |
| if {!$opts(-nocomplain)} { |
| append result "\#\# No known namespace $arg\n" |
| set code error |
| } |
| } |
| if {[string compare $names {}]} { |
| set count 0 |
| foreach name [lsort $names] { |
| append result "namespace eval $name \{;\n\n" |
| if {$opts(-vars)} { |
| set vars [lremove [namespace eval $name info vars] \ |
| [info globals]] |
| append result [namespace eval $name \ |
| [namespace current]::dump_variable [lsort $vars]]\n |
| } |
| set procs [namespace eval $name info procs] |
| if {$opts(-procs)} { |
| set export [namespace eval $name namespace export] |
| if {[string compare $export {}]} { |
| append result "namespace export -clear $export\n\n" |
| } |
| append result [namespace eval $name \ |
| [namespace current]::dump_proc [lsort $procs]] |
| } |
| if {$opts(-imports)} { |
| set cmds [info commands ${name}::*] |
| regsub -all ${name}:: $cmds {} cmds |
| set cmds [lremove $cmds $procs] |
| foreach cmd [lsort $cmds] { |
| set cmd [namespace eval $name \ |
| [list namespace origin $cmd]] |
| if {[string compare $name \ |
| [namespace qualifiers $cmd]]} { |
| ## Yup, it comes from somewhere else |
| append result [list namespace import -force $cmd] |
| } else { |
| ## It is probably an alias |
| set alt [interp alias {} $cmd] |
| if {[string compare $alt {}]} { |
| append result "interp alias {} $cmd {} $alt" |
| } else { |
| append result "# CANNOT HANDLE $cmd" |
| } |
| } |
| append result \n |
| } |
| append result \n |
| } |
| if {$opts(-recursive)} { |
| append result [uplevel [namespace current]::dump_namespace\ |
| [namespace children $name]] |
| } |
| append result "\}; # end of namespace $name\n\n" |
| } |
| } elseif {!$opts(-nocomplain)} { |
| append result "\#\# No known namespace $arg\n" |
| set code error |
| } |
| } |
| return -code $code [string trimright $result \n] |
| } |
| |
| # dump_widget -- |
| # Outputs a widget configuration in source'able but human readable form. |
| # Arguments: |
| # args comments |
| # Results: |
| # Returns widget configuration in "source"able form. |
| # |
| proc dump_widget {args} { |
| if {[string match {} [info command winfo]]} { |
| return -code error "winfo not present, cannot dump widgets" |
| } |
| array set opts { |
| -nocomplain 0 -filter .* -default 0 |
| } |
| set args [get_opts opts $args {-nocomplain 0 -filter 1 -default 0} \ |
| {-filter regexp}] |
| if {[string match {} $args]} { |
| if {$opts(-nocomplain)} { |
| return |
| } else { |
| return -code error "wrong \# args: dump widget ?-nocomplain?\ |
| ?-default? ?-filter regexp? ?--? pattern ?pattern ...?" |
| } |
| } |
| set code ok |
| set result {} |
| foreach arg $args { |
| if {[string compare {} [set ws [info command $arg]]]} { |
| foreach w [lsort $ws] { |
| if {[winfo exists $w]} { |
| if {[catch {$w configure} cfg]} { |
| append result "\#\# Widget $w\ |
| does not support configure method" |
| if {!$opts(-nocomplain)} { |
| set code error |
| } |
| } else { |
| append result "\#\# [winfo class $w] $w\n$w configure" |
| foreach c $cfg { |
| if {[llength $c] != 5} continue |
| ## Filter options according to user provided |
| ## filter, and then check to see that they |
| ## are a default |
| if {[regexp -nocase -- $opts(-filter) $c] && \ |
| ($opts(-default) || [string compare \ |
| [lindex $c 3] [lindex $c 4]])} { |
| append result " \\\n\t[list [lindex $c 0]\ |
| [lindex $c 4]]" |
| } |
| } |
| append result \n |
| } |
| } |
| } |
| } elseif {!$opts(-nocomplain)} { |
| append result "\#\# No known widget $arg\n" |
| set code error |
| } |
| } |
| return -code $code [string trimright $result \n] |
| } |
| |
| # dump_canvas -- |
| # |
| # ADD COMMENTS HERE |
| # |
| # Arguments: |
| # args comments |
| # Results: |
| # Returns ... |
| # |
| proc dump_canvas {args} { |
| if {[string match {} [info command winfo]]} { |
| return -code error "winfo not present, cannot dump widgets" |
| } |
| array set opts { |
| -nocomplain 0 -default 0 -configure 0 -filter .* |
| } |
| set args [get_opts opts $args {-nocomplain 0 -filter 1 -default 0 \ |
| -configure 0} {-filter regexp}] |
| if {[string match {} $args]} { |
| if {$opts(-nocomplain)} { |
| return |
| } else { |
| return -code error "wrong \# args: dump canvas ?-nocomplain?\ |
| ?-configure? ?-default? ?-filter regexp? ?--? pattern\ |
| ?pattern ...?" |
| } |
| } |
| set code ok |
| set result {} |
| foreach arg $args { |
| if {[string compare {} [set ws [info command $arg]]]} { |
| foreach w [lsort $ws] { |
| if {[winfo exists $w]} { |
| if {[string compare Canvas [winfo class $w]]} { |
| append result "\#\# Widget $w is not a canvas widget" |
| if {!$opts(-nocomplain)} { |
| set code error |
| } |
| } else { |
| if {$opts(-configure)} { |
| append result [dump_widget -filter $opts(-filter) \ |
| [expr {$opts(-default)?{-default}:{-no}}] \ |
| $w] |
| append result \n |
| } else { |
| append result "\#\# Canvas $w items\n" |
| } |
| ## Output canvas items in numerical order |
| foreach i [lsort -integer [$w find all]] { |
| append result "\#\# Canvas item $i\n" \ |
| "$w create [$w type $i] [$w coords $i]" |
| foreach c [$w itemconfigure $i] { |
| if {[llength $c] != 5} continue |
| if {$opts(-default) || [string compare \ |
| [lindex $c 3] [lindex $c 4]]} { |
| append result " \\\n\t[list [lindex $c 0]\ |
| [lindex $c 4]]" |
| } |
| } |
| append result \n |
| } |
| } |
| } |
| } |
| } elseif {!$opts(-nocomplain)} { |
| append result "\#\# No known widget $arg\n" |
| set code error |
| } |
| } |
| return -code $code [string trimright $result \n] |
| } |
| |
| # dump_text -- |
| # |
| # ADD COMMENTS HERE |
| # |
| # Arguments: |
| # args comments |
| # Results: |
| # Returns ... |
| # |
| proc dump_text {args} { |
| if {[string match {} [info command winfo]]} { |
| return -code error "winfo not present, cannot dump widgets" |
| } |
| array set opts { |
| -nocomplain 0 -default 0 -configure 0 -start 1.0 -end end |
| } |
| set args [get_opts opts $args {-nocomplain 0 -default 0 \ |
| -configure 0 -start 1 -end 1}] |
| if {[string match {} $args]} { |
| if {$opts(-nocomplain)} { |
| return |
| } else { |
| return -code error "wrong \# args: dump text ?-nocomplain?\ |
| ?-configure? ?-default? ?-filter regexp? ?--? pattern\ |
| ?pattern ...?" |
| } |
| } |
| set code ok |
| set result {} |
| foreach arg $args { |
| if {[string compare {} [set ws [info command $arg]]]} { |
| foreach w [lsort $ws] { |
| if {[winfo exists $w]} { |
| if {[string compare Text [winfo class $w]]} { |
| append result "\#\# Widget $w is not a text widget" |
| if {!$opts(-nocomplain)} { |
| set code error |
| } |
| } else { |
| if {$opts(-configure)} { |
| append result [dump_widget -filter $opts(-filter) \ |
| [expr {$opts(-default)?{-default}:{-no}}] \ |
| $w] |
| append result \n |
| } else { |
| append result "\#\# Text $w dump\n" |
| } |
| catch {unset tags} |
| catch {unset marks} |
| set text {} |
| foreach {k v i} [$w dump $opts(-start) $opts(-end)] { |
| switch -exact $k { |
| text { |
| append text $v |
| } |
| window { |
| # must do something with windows |
| # will require extra options to determine |
| # whether to rebuild the window or to |
| # just reference it |
| append result "#[list $w] window create\ |
| $i [$w window configure $i]\n" |
| } |
| mark {set marks($v) $i} |
| tagon {lappend tags($v) $i} |
| tagoff {lappend tags($v) $i} |
| default { |
| error "[info level 0]:\ |
| should not be in this switch arm" |
| } |
| } |
| } |
| append result "[list $w insert $opts(-start) $text]\n" |
| foreach i [$w tag names] { |
| append result "[list $w tag configure $i]\ |
| [$w tag configure $i]\n" |
| if {[info exists tags($i)]} { |
| append result "[list $w tag add $i]\ |
| $tags($i)\n" |
| } |
| foreach seq [$w tag bind $i] { |
| append result "[list $w tag bind $i $seq \ |
| [$w tag bind $i $seq]]\n" |
| } |
| } |
| foreach i [array names marks] { |
| append result "[list $w mark set $i $marks($i)]\n" |
| } |
| } |
| } |
| } |
| } elseif {!$opts(-nocomplain)} { |
| append result "\#\# No known widget $arg\n" |
| set code error |
| } |
| } |
| return -code $code [string trimright $result \n] |
| } |
| |
| # dump_interface -- NOT FUNCTIONAL |
| # |
| # the end-all-be-all of Tk dump commands. This should dump the widgets |
| # of an interface with all the geometry management. |
| # |
| # Arguments: |
| # args comments |
| # Results: |
| # Returns ... |
| # |
| proc dump_interface {args} { |
| |
| } |
| |
| # dump_state -- |
| # |
| # This dumps the state of an interpreter. This is primarily a wrapper |
| # around other dump commands with special options. |
| # |
| # Arguments: |
| # args comments |
| # Results: |
| # Returns ... |
| # |
| proc dump_state {args} { |
| |
| } |
| |
| |
| ## Force the parent namespace to include the exported commands |
| ## |
| catch {namespace eval ::Utility namespace import -force ::Utility::dump::*} |
| |
| }; # end of namespace ::Utility::dump |
| |
| return |