GPS: I'm sick of the discussion over the expand vs. {} vs. {*} and so on, so I came up with this. Comments are welcome...
GPS: To help the reader understand this (after a comment in Tcl'ers chat). The ~ token is discarded in a safe.eval call. ~ is like the proposed ` except it must be preceded and followed by whitespace. Any list or string after a ~ token is expanded. Simple no mess solution to a simple problem. The character is easily changed if you like.
I have added tests to demonstrate how it solves the problems of whitespace in widget pathnames, and potentially dangerous commands being executed.
GPS: Sep 2, 2003 -- I have improved version 5 of safe.eval in several ways. Rather than having to use two tildes like: ~SPACE~SPACE to pass a single tilde you may now use ~~. This fits in nicely with other tools that react to %% as meaning a literal %. I have left version 4 because the tests are pretty much the same, and I thought someone might find the difference interesting.
#Copyright 2003 George Peter Staplin #You may use this under the same terms as Tcl. #Thanks to RS and AM for comments via the Tcl'ers Chat. #rev 5 proc safe.eval args { set cmd [list] set expand 0 foreach arg $args { if {"~~" == $arg} { lappend cmd ~ set expand 0 } elseif {"~" == $arg} { set expand 1 } elseif {$expand} { foreach a $arg { lappend cmd $a } set expand 0 } else { lappend cmd $arg set expand 0 } } uplevel 1 $cmd }
#Copyright 2003 George Peter Staplin #You may use this under the same terms as Tcl. #Thanks to RS and AM for comments via the Tcl'ers Chat. #rev 4 proc safe.eval args { set expand 0 set cmd [list] foreach arg $args { if {"~" == $arg} { if {$expand} { #we have ~ ~ lappend cmd ~ set expand 0 continue } set expand 1 } else { if {$expand} { set cmd [concat $cmd $arg] } else { lappend cmd $arg } set expand 0 } } uplevel 1 $cmd } proc A {a b c} { puts A: foreach v [list a b c] { puts " $v: [set $v]" } } proc B {a b c d} { puts B: foreach v [list a b c d] { puts " $v: [set $v]" } } proc C {a b c} { puts C: foreach v [list a b c] { puts " $v: [set $v]" } } proc main {} { #Let's do some simple expansion #A expects 3 arguments safe.eval A ~ [list 1 2 3] #Now let's use a potentially dangerous command set l [list hello world bye] #We want $l to expand, but not [dangerous] #B expects 4 arguments safe.eval B {[dangerous]} ~ $l set l [list hi world] #exec shouldn't be called safe.eval C ~ $l "\[exec\]" puts "Now testing with a typical Tk usage..." package require Tk button .b button .b2 button .b3 puts BEFORE:[winfo children .] safe.eval destroy ~ [winfo children .] puts AFTER:[winfo children .] #Now for a commonly complained about issue... #The case of a window pathname with a space in it. #In the normal eval this would be clobbered, but not with safe.eval. set flags "-text Exit -command exit " append flags "-bg orange -fg yellow" set win ".b ob" safe.eval button $win ~ $flags -bd 0 pack $win } main