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