[GRASS5] [bug #2488] (grass) GRASS 5.7 WISH - improve string reading ability in g.parser

Glynn Clements glynn.clements at virgin.net
Mon Aug 2 20:00:41 EDT 2004


guest user via RT wrote:

> e.g. 5.7's auto-gen Tcl makes:
> button .run -text "Run" -command {
>  ...
>     set cmd "| $cmd 2>@ stdout"
>     catch {open $cmd r} msg
>  ...
> }

A good example of how *not* to to construct command lines in Tcl (or,
for that matter, most languages).

A Unix command consists of a list of strings, not a single string. 
There are defined boundaries between arguments, and these need to be
preserved. Fortunately, Tcl's open and exec functions accept Tcl
lists, which makes this relatively easy to achieve (unlike e.g. 
system()).

Tcl lists should be constructed and manipulated using the appropriate
functions, e.g. list, lappend, concat etc. They should not be
constructed by including variable expansions in a double-quoted
string.

E.g. in the above, the line:

	set cmd "| $cmd 2>@ stdout"

should be written as:

	set cmd [concat | $cmd 2>@ stdout]

I.e.:
	$ tclsh
	% set cmd {d.text.freetype {text=abc 123} path=font.ttf}
	$ puts $cmd
	d.text.freetype {text=abc 123} path=font.ttf
	% foreach x $cmd {puts $x}
	d.text.freetype
	text=abc 123
	path=font.ttf
	% set cmd [concat | $cmd 2>@ stdout]
	% puts $cmd
	| d.text.freetype {text=abc 123} path=font.ttf 2>@ stdout
	% foreach x $cmd {puts $x}
	|
	d.text.freetype
	text=abc 123
	path=font.ttf
	2>@
	stdout
	% 

However, that specific change will only help if the original value of
$cmd is already a well-formed list (i.e. where "text=abc 123" is a
single item rather than a pair of items). Given that most of the Tcl
command-line handling (in both tcltkgrass and G_gui()) which I have
seen is similarly broken, there are probably other places which also
need to be fixed.

> so:
>  set cmd {d.text.freetype text="abc 123" path=font.ttf}
> gives argc=4 in G_parser
> 
> argv[0] is 'd.text.freetype'
> argv[1] is 'text="abc'
> argv[2] is '123"'
> argv[3] is 'path=font.ttf'

Which is correct. You've set cmd to a list which contains four
elements, which correspond to the four argv[] values above.

> and
> 
>  set cmd {d.text.freetype text="abc_123" path=font.ttf}
> gives argc=3 (and works)
> 
> so it is never filtered through a shell for option="a b" to be made one item.
> The $cmd string is formed correctly; but not passed to G_parser() correctly.

No it isn't formed correctly. exec (and open where the first word is
the pipe symbol) treat their argument as a *list*, not a string. Well,
everything in Tcl is a string, but it has specific semantics for
strings which represent lists, i.e. any elements which contain spaces
will be contained within braces.

If you construct the *list* using list manipulation functions such as
list, lappend, concat etc, Tcl will preserve the list structure. E.g.

	% set arg1 {d.text.freetype}
	% set arg2 {text=abc 123}
	% set arg3 {path=font.ttf}
	% set string "$arg1 $arg2 $arg3"
	% puts $string
	d.text.freetype text=abc 123 path=font.ttf
	% foreach x $string {puts $x}
	d.text.freetype
	text=abc
	123
	path=font.ttf
	% set list [list $arg1 $arg2 $arg3]
	% puts $list
	d.text.freetype {text=abc 123} path=font.ttf
	% foreach x $list {puts $x}
	d.text.freetype
	text=abc 123
	path=font.ttf

-- 
Glynn Clements <glynn.clements at virgin.net>




More information about the grass-dev mailing list