# 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