blob: f99729696d8bee5c9c742e219469e2c560d9b505 [file] [log] [blame]
# 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