AMG: Here is a Tcl 8.5-compatible implementation of tcl::prefix that passes the 8.6.8 test suite. It uses throw, so also use forward-compatible try and throw to make it actually work in 8.5.
proc tcl::prefix {subcommand args} { switch $subcommand { a - al - all { # Process arguments. if {[llength $args] != 2} { throw {TCL WRONGARGS} "wrong # args: should be\ \"tcl::prefix all table string\"" } lassign $args table string # Return list of all strings with the given prefix. lsearch -all -inline $table [regsub -all {[][?*\\]} $string {\\&}]* } l - lo - lon - long - longe - longes - longest { # Process arguments. if {[llength $args] != 2} { throw {TCL WRONGARGS} "wrong # args: should be\ \"tcl::prefix longest table string\"" } lassign $args table string # Search for the longest common prefix. foreach entry $table { if {[string equal -length [string length $string] $entry $string]} { if {![info exists common]} { set common $entry } else { for {set i 0} {$i < [string length $common] && $i < [string length $entry]} {incr i} { if {[string index $common $i] ne [string index $entry $i]} { break } } set common [string range $common 0 [expr {$i - 1}]] } } } # Return the longest common prefix, or empty string if no matches. if {[info exists common]} { return $common } } m - ma - mat - matc - match { # Process arguments. if {[llength $args] < 2} { throw {TCL WRONGARGS} "wrong # args: should be\ \"tcl::prefix match ?options? table string\"" } lassign [lrange $args end-1 end] table string set args [lrange $args 0 end-2] set message option while {[llength $args]} { set args [lassign $args arg] switch $arg { -ex - -exa - -exac - -exact { # -exact switch. set exact {} } -m - -me - -mes - -mess - -messa - -messag - -message { # -message switch. Next argument is the message string. if {![llength $args]} { throw {TCL OPERATION NOARG} "missing value for -message" } set args [lassign $args message] } -er - -err - -erro - -error { # -error switch. Next argument is the error options dict. if {![llength $args]} { throw {TCL OPERATION NOARG} "missing value for -error" } set args [lassign $args options] if {[llength $options] & 1} { throw {TCL VALUE DICTIONARY} "error options must have an\ even number of elements" } } -e { # Ambiguous switch. throw [list TCL LOOKUP INDEX option $arg] "ambiguous option\ \"$arg\": must be -error, -exact, or -message" } default { # Invalid switch. throw [list TCL LOOKUP INDEX option $arg] "bad option\ \"$arg\": must be -error, -exact, or -message" }} } # Always accept exact match, no questions asked, even if it happens to # also be the prefix for another string in the table. if {$string in $table} { return $string } # Attempt prefix matching unless -exact was used. Accept a prefix match # if unambiguous. if {![info exists exact]} { set matches [prefix all $table $string] if {[llength $matches] == 1} { return [lindex $matches 0] } } # Match failed. Assemble and return the error result. if {![info exists exact] && [llength $matches]} { set message "ambiguous $message \"$string\": " } else { set message "bad $message \"$string\": " } if {![llength $table]} { append message "no valid options" } else { if {[llength $table] > 1} { lset table end "or [lindex $table end]" } append message "must be [join $table\ {*}[if {[llength $table] > 2} {list ", "}]]" } if {![info exists options]} { set options [list -level 0 -code error\ -errorcode [list TCL LOOKUP INDEX $message $string]] } if {![dict size $options]} { set message {} } elseif {![dict exists $options -code]} { dict set options -code error } dict incr options -level return {*}$options $message } default { # Invalid subcommand. throw [list TCL LOOKUP SUBCOMMAND $arg] "unknown or ambiguous\ subcommand \"$arg\": must be all, longest, or match" }} }