| # util-expand.tcl -- |
| # |
| # This file implements package ::Utility::expand, 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::expand 1.0 |
| |
| namespace eval ::Utility::expand {; |
| |
| namespace export -clear expand* |
| namespace import -force ::Utility::* |
| |
| ## |
| ## NOTE: In places where uplevel is used, it is highly likely that |
| ## a further eval redirect is otherwise necessary for foreign interps |
| ## |
| |
| # expand -- |
| # |
| # The string to match is expanded to the longest possible match. |
| # If data(-showmultiple) is non-zero and the user longest match |
| # equaled the string to expand, then all possible matches are |
| # output to stdout. Triggers bell if no matches are found. |
| # |
| # Arguments: |
| # type type of expansion (path / proc / variable) |
| # |
| # Returns: |
| # number of matches found |
| # |
| proc expand {args} { |
| array set opts { |
| -type any -widget {} |
| } |
| set args [get_opts opts $args {-type 1 -widget 1} {-widget widget}] |
| if {[string match {} $opts(-widget)] && [llength $args]!=1} { |
| return -code error "wrong # args: should be\ |
| \"[lindex [info level 0] 0] ?-type type?\ |
| ?-widget widget || str?" |
| } |
| set prefix [namespace current]::expand_ |
| if {[string match {} [set arg [info commands $prefix$opts(-type)]]]} { |
| set arg [info commands $prefix$opts(-type)*] |
| } |
| set result {} |
| set code ok |
| if 0 { |
| set exp "\[^\\]\[ \t\n\r\[\{\"\$]" |
| set tmp [$w search -backwards -regexp $exp insert-1c limit-1c] |
| if {[string compare {} $tmp]} {append tmp +2c} else {set tmp limit} |
| if {[$w compare $tmp >= insert]} return |
| set str [$w get $tmp insert] |
| } |
| 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\ |
| \"$opts(-type)\", must be one of: [join [lsort $arg] {, }]" |
| } |
| default { |
| regsub -all $prefix $arg {} arg |
| return -code error "ambiguous type \"$opts(-type)\",\ |
| could be one of: [join [lsort $arg] {, }]" |
| } |
| } |
| if 0 { |
| set len [llength $res] |
| if {$len} { |
| $w delete $tmp insert |
| $w insert $tmp [lindex $res 0] |
| if {$len > 1} { |
| upvar \#0 [namespace current]::[winfo parent $w] data |
| if {$data(-showmultiple) && \ |
| ![string compare [lindex $res 0] $str]} { |
| puts stdout [lsort [lreplace $res 0 0]] |
| } |
| } |
| } else { bell } |
| return [incr len -1] |
| } |
| return -code $code [string trimright $result \n] |
| } |
| |
| # expand_pathname -- |
| # |
| # expand a file pathname based on $str |
| # This is based on UNIX file name conventions |
| # |
| # Arguments: |
| # str partial file pathname to expand |
| # Results: |
| # Returns list containing longest unique match followed by all the |
| # possible further matches |
| # |
| proc expand_pathname {str} { |
| #reval pwd, cd, glob and final cd |
| set pwd [pwd] |
| if {[catch {cd [file dirname $str]} err]} { |
| return -code error $err |
| } |
| if {[catch {glob [file tail $str]*} m]} { |
| set match {} |
| } else { |
| if {[llength $m] > 1} { |
| global tcl_platform |
| if {[string match windows $tcl_platform(platform)]} { |
| ## Windows is screwy because it can be case insensitive |
| set tmp [best_match [string tolower [lsort $m]] \ |
| [string tolower [file tail $str]]] |
| } else { |
| set tmp [best_match [lsort $m] [file tail $str]] |
| } |
| if {[string match ?*/* $str]} { |
| set tmp [file dirname $str]/$tmp |
| } elseif {[string match /* $str]} { |
| set tmp /$tmp |
| } |
| regsub -all { } $tmp {\\ } tmp |
| set match [linsert $m 0 $tmp] |
| } else { |
| ## This may look goofy, but it handles spaces in path names |
| eval append match $m |
| if {[file isdir $match]} {append match /} |
| if {[string match ?*/* $str]} { |
| set match [file dirname $str]/$match |
| } elseif {[string match /* $str]} { |
| set match /$match |
| } |
| regsub -all { } $match {\\ } match |
| ## Why is this one needed and the ones below aren't!! |
| set match [list $match] |
| } |
| } |
| cd $pwd |
| return $match |
| } |
| |
| # expand_proc -- |
| # |
| ## ExpandProcname - expand a tcl proc name based on $str |
| # ARGS: str - partial proc name to expand |
| # Calls: best_match |
| # Returns: list containing longest unique match followed by all the |
| # possible further matches |
| # |
| # Arguments: |
| # args comments |
| # Results: |
| # Returns ... |
| # |
| proc expand_proc {str} { |
| #reval info |
| set match [uplevel info commands [list $str]*] |
| if {[llength $match] > 1} { |
| regsub -all { } [best_match $match $str] {\\ } str |
| set match [linsert $match 0 $str] |
| } else { |
| regsub -all { } $match {\\ } match |
| } |
| return $match |
| } |
| |
| # expand_variable -- |
| # |
| ## ExpandVariable - expand a tcl variable name based on $str |
| # ARGS: str - partial tcl var name to expand |
| # Calls: best_match |
| # Returns: list containing longest unique match followed by all the |
| # possible further matches |
| # |
| # Arguments: |
| # args comments |
| # Results: |
| # Returns ... |
| # |
| proc expand_variable {str} { |
| #reval "array names", "info vars" |
| if {[regexp {([^\(]*)\((.*)} $str junk ary str]} { |
| ## Looks like they're trying to expand an array. |
| set match [array names $ary $str*] |
| if {[llength $match] > 1} { |
| set vars $ary\([best_match $match $str] |
| foreach var $match {lappend vars $ary\($var\)} |
| return $vars |
| } else {set match $ary\($match\)} |
| ## Space transformation avoided for array names. |
| } else { |
| set match [info vars $str*] |
| if {[llength $match] > 1} { |
| regsub -all { } [best_match $match $str] {\\ } str |
| set match [linsert $match 0 $str] |
| } else { |
| regsub -all { } $match {\\ } match |
| } |
| } |
| return $match |
| } |
| |
| }; # end of namespace ::Utility::expand |