Writing case insensitive procedures using unknown

Case Insensitive TCL commands

MaheshD: Some days back I faced an issue with writing case insensitive procedure names.

    proc abc {} {
        puts "Called function abc() .."
    }

This should work with other 7 variants ABC, abC, aBc, aBC, Abc, AbC, aBc.

The solution to this could be to use the unknown function in TCL.

# define the original command name list.

    set orig_cmd_names [list abc]

# Rename the original unknown to unknown_orig

    rename unknown unknown_orig

# Define a customized unknown procedure.

    proc unknown {args} {
        set cmd [lindex $args 0]
        global orig_cmd_names
        foreach oCmd $orig_cmd_names {
            if {[string equal -nocase $oCmd $cmd]} {
                interp alias "" $cmd "" $oCmd
                tailcall $oCmd {*} [lrange $args 1 end]
            }
        }
        # Not found .. :-(, call the base unknown function.
        unknown_orig $args
    }

    %abC
    Called function abc() ..
    %Abc
    Called function abc() ..

MG would recommend using string equal instead of regexp to compare the strings, otherwise you could get inaccurate matches when command names contain chars with special meaning for regexp (proc a..z).

WK Actually regexp would have its own cool flavor - imagine you can't remember part of a command - you'd just write tcl::Copy.* and it would match proper command for you. Here's a more complete example:

    rename unknown unknown_orig

    proc unknown {args} {
        set cmd [lindex $args 0]
        set parent [join [lrange [split $cmd :] 0 end-2] :]
        set tail [namespace tail $cmd]
        set found [list]
        foreach ncmd [info commands ::${parent}::*] {
            if {[regexp -nocase $cmd "^$ncmd\$"]} {
                lappend found $ncmd
            }
        }
        if {[llength $found] == 0} {
            unknown_orig $args
        }  elseif {[llength $found] == 1} {
            tailcall [lindex $found 0] {*}[lrange $args 1 end]
        }  else  {
            error "Command $cmd matched multiple command - [join $found]."
        }
    }

It even fits this wiki page as it still does it in case insensitive way

AMG: Probably [string match -nocase] would be more suited for this application.

MJ would recommend to install interp aliases for the other capitalisations as well so subsequent calls to the different capitalisation don't invoke unknown. Executing the command in global scope is probably an error anyway. It will fail nicely if the original command uses uplevel or upvar for instance.

AMG: This sounds like a job for tailcall!! Also I implemented MG's and MJ's suggestions.


MaheshD - 2010-03-23 00:05:47

MG, MJ and AMG, that was very helpful..

Thanks for your overwhelming response..!