Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

argv processing in tcl

721 views
Skip to first unread message

Chris Nelson

unread,
Aug 13, 1997, 3:00:00 AM8/13/97
to

I'm porting some csh to tcl. I've got blocks of code like:

while ($#argv > 0)
switch ("$argv[1]")
case -xy: # Obsolete -- does nothing
shift
if ($#argv == 0) then
echo "$prog ERROR: Missing XY"
goto error_exit
endif
set xy = "$argv[1]"
breaksw
...

endsw

shift
end # while


My first pass at porting is kind of line-by-line and I'm not quite
satisfied:

set arg_index 0
while {$arg_index < $argc} {
set arg_switch [lindex $argv $arg_index]

switch -- $arg_switch {
-xy {
incr arg_index 1
if {$arg_index >= $argc} {
puts "$script_name ERROR: Missing XY"
error_exit
}
set xy [lindex $argv $arg_index]
}
...
} ;# switch
} ; # while


It seems to me that all the lindexing, etc. may be quite inefficient.
I'm inclined to translate this to tcl concepts with something like:

set arg_index [lindex $argv "-xy"]
if { $arg_index != 0 } {
if {arg_index == $argc} {
... error stuff ...
}
set xy [lindex $argv [incr $arg_index]]
}


I think that I should also delete the processed arguments from argv (or
a copy) as I go to make subsequent searches faster, etc. Can anyone
here give me suggestions on the "right" way to handle command line
arguments in tcl? I would like to use tcl paradigms where it makes
sense to make clear, efficient code.

Thanks.

Chris

--
Chris Nelson Rens-se-LEER is a county.
Work: nel...@pinebush.com RENS-se-ler is a city.
Play: chris....@juno.com R-P-I is a school in Troy!

Cameron Laird

unread,
Aug 14, 1997, 3:00:00 AM8/14/97
to

In article <33F200...@pinebush.com>,
Chris Nelson <nel...@pinebush.com> wrote:
.
.
.

>I think that I should also delete the processed arguments from argv (or
>a copy) as I go to make subsequent searches faster, etc. Can anyone
>here give me suggestions on the "right" way to handle command line
>arguments in tcl? I would like to use tcl paradigms where it makes
>sense to make clear, efficient code.
.
.
.
One approach that's in the spirit of Tcl is to use work that
others have already done. The two examples I know appear in
<URL:http://starbase.neosoft.com/~claird/comp.lang.tcl/tcl.html#getargs>.
--

Cameron Laird http://starbase.neosoft.com/~claird/home.html
cla...@NeoSoft.com +1 713 996 8546 FAX

Chris Nelson

unread,
Aug 15, 1997, 3:00:00 AM8/15/97
to

Heribert Dahms wrote:
>
> In <33F200...@pinebush.com> nel...@pinebush.com (Chris Nelson) writes:
>
> : while {$arg_index < $argc} {

> : set arg_switch [lindex $argv $arg_index]
> :
> : switch -- $arg_switch {
>
> The following is simple, readable and should be efficient:
>
> foreach arg_switch $argv {
> switch -- $arg_switch {
>
> Bye, Heribert (da...@ifk20.mach.uni-karlsruhe.de)


But if my command line looks like:

app -foo fooval -bar barval ...

When I find "-foo" and I want to get the value for "-foo" from the NEXT
item in $argv, how do I advance $arg_switch?

Heribert Dahms

unread,
Aug 15, 1997, 3:00:00 AM8/15/97
to

Heribert Dahms

unread,
Aug 19, 1997, 3:00:00 AM8/19/97
to

In <33F439...@pinebush.com> nel...@pinebush.com (Chris Nelson) writes:

: Heribert Dahms wrote:
: > foreach arg_switch $argv {
: > switch -- $arg_switch {

: But if my command line looks like:


:
: app -foo fooval -bar barval ...
:
: When I find "-foo" and I want to get the value for "-foo" from the NEXT
: item in $argv, how do I advance $arg_switch?

I forgot about this, which will complicate things.
Sorry, I've no time to provide you with a working solution.


Bye, Heribert (da...@ifk20.mach.uni-karlsruhe.de)

Jay Sekora

unread,
Aug 19, 1997, 3:00:00 AM8/19/97
to

Unfortunately, the original article has expired at my ISP. If you're
trying to parse option pairs like those taken by many of the Tcl and
Tk commands, like so:

command -option1 value1 -option2 value2

(where the options and corresponding values are actually optional),
the following procedure (from jstools, described at
http://www.aq.org/~js/js-jstools.html ) will do:

######################################################################
# j:parse_args arglist - parse arglist in parent procedure
# arglist is a list of option names (without leading "-");
# this proc puts their values (if any) into variables (named after
# the option name) in d parent procedure
# any element of arglist can also be a list consisting of an option
# name and a default value.
######################################################################

proc j:parse_args { arglist } {
upvar args args

# tcl-8.0a1 kludge:
set arglist [string trim $arglist " \n"]
foreach pair $arglist {
set option [lindex $pair 0]
set default [lindex $pair 1] ;# will be null if not supplied
set index [lsearch -exact $args "-$option"]
if {$index != -1} {
set index1 [expr {$index + 1}]
set value [lindex $args $index1]
uplevel 1 [list set $option $value] ;# caller's variable "$option"
set args [lreplace $args $index $index1]
} else {
uplevel 1 [list set $option $default] ;# caller's variable "$option"
}
}
return 0
}

Note the use of uplevel (I could also have used upvar) to set the
value in the calling procedure's context, rather than in the context
of j:parse_args itself.

You would call it like this:

proc j:prompt_colour_name { args } {
j:parse_args {
{prompt {Choose a colour:}}
{title {Colour Name Selector}}
{default magenta}
}

puts stderr "prompt was $prompt"
puts stderr "title was $title"
puts stderr "default was $default"
}

and then you would call the procedure that uses j:parse_args like
so:

j:prompt_colour_name -prompt "What colour are your socks?"

I like the fact that this procedure forces me to name all the options,
and use those names when providing options, because I think it makes
my code more legible.

Cheers,

Jay

--
Jay Sekora
j...@aq.org
http://www.aq.org/~js/

Laurent Demailly

unread,
Aug 19, 1997, 3:00:00 AM8/19/97
to

If you use Tcl8, you might want to give a try to an unsupported,
undocumented and slow package (what a nice list of 'features' isn't it !
but all the above can be improved if the community feedback is positive) :
"opt0.1"

Behind this cryptic name you'll find a quite powerful arguments
and flags parsing engine. It is oriented toward proc's argument
parsing but this can be applied easily to your program parsing,
you could for instance do the following :
(Demo)
----8<----
#! /bin/sh
# restart with tclsh8 \
exec tclsh8.0 "$0" "$@"

package require opt 0.1
::tcl::OptProc main {
{-foo defaultvalue "Some string"}
{-bar 6 "Some integer"}
{-toto -choice {a b c} "Some 'one of' choice"}
{-flag "A boolean flag without value"}
} {
puts "foo=$foo"
puts "bar=$bar"
puts "toto=$toto"
puts "flag=$flag"
}

if {[catch {eval main $argv} msg]} {
puts stderr $msg
}
----8<----
save the above to the file "opttest" for instance and chmod 755, then try :
$ opttest
foo=defaultval
bar=6
toto=a
flag=0
$ opttest -foo
no value given for parameter "-foo" (use -help for full usage) :
-foo string (defaultval) Some string
$ opttest -bar 2.3
bad value "2.3" for parameter
-bar int (6) Some integer
$ opttest -toto b -flag -foo "an other one" -bar -5
foo=an other one
bar=-5
toto=b
flag=1
$ opttest -help
Usage information:
Var/FlagName Type Value Help
------------ ---- ----- ----
( -help gives this help )
-foo string (defaultval) Some string
-bar int (6) Some integer
-toto choice (a b c) Some 'one of' choice
-flag boolflag (false) A boolean flag without value


You will find more examples in tests/opt.test, library/safe.tcl
and in the source code itself in library/opt0.1/optparse.tcl

Feel free to mail me if something is unclear, needs improvements
or if you have a bug report about that particular package.

--
Laurent Demailly (dl) * Sun labs * SunScript - Tcl/Tk team


Cameron Laird

unread,
Aug 19, 1997, 3:00:00 AM8/19/97
to

In article <slrn5vi0...@aq.org>, Jay Sekora <j...@aq.org> wrote:
>Unfortunately, the original article has expired at my ISP. If you're
>trying to parse option pairs like those taken by many of the Tcl and
>Tk commands, like so:
>
> command -option1 value1 -option2 value2
>
>(where the options and corresponding values are actually optional),
>the following procedure (from jstools, described at
>http://www.aq.org/~js/js-jstools.html ) will do:
>
>######################################################################
># j:parse_args arglist - parse arglist in parent procedure
># arglist is a list of option names (without leading "-");
># this proc puts their values (if any) into variables (named after
># the option name) in d parent procedure
># any element of arglist can also be a list consisting of an option
># name and a default value.
>######################################################################
>
>proc j:parse_args { arglist } {
> upvar args args
.
.
.
Kudos to Jay for making this generally available.

I'm a strong believer of including a well-chosen example
or two in in-line comments. I claim 'twould be valuable
to augment this header with a few lines on the order of

# Example: the result of
# set args "-abc xxx -cdef yyyy final"
# j:parse_args {{abc a} {cdef b} {ghij c}}
# is the same as though, in that same context,
# the assignments
# set abc xxx; set cdef yyyy; set ghij c
# set args final
# had been made.

Michael Salmon

unread,
Aug 19, 1997, 3:00:00 AM8/19/97
to

Heribert Dahms wrote:
>
> In <33F439...@pinebush.com> nel...@pinebush.com (Chris Nelson) writes:
>
> : Heribert Dahms wrote:
> : > foreach arg_switch $argv {
> : > switch -- $arg_switch {
>
> : But if my command line looks like:
> :
> : app -foo fooval -bar barval ...
> :
> : When I find "-foo" and I want to get the value for "-foo" from the NEXT
> : item in $argv, how do I advance $arg_switch?

You need a state machine (N.B. there are 0,1 and 2 parameters after the
switch) :

set argState none
foreach arg $argv {
switch -exact -- $argState {
loadlib {
set loadlib $arg
set argState loadinit
}
loadinit {
load $loadlib $arg
set argState none
}
rc {
set rcfile $arg
set argState none
}
dest usage
none {
switch -regexp -- $arg {
-char(set)? { set argState charset }
-cs { set argState charset }
-key(map)? { set argState keymap }
-lib(rary)? { set argState lib }
-load { set argState loadlib }
-model { set argState model }
-rc(file)? { set argState rc }
-v(erbose)? { set verbose 1 }
default {
set cmdDest $arg
set argState dest
}
}
}
default {
set cmdOpt($argState) $arg
set argState none
}
}
}

if { $argState != "none" && $argState != "dest" } {usage}

The tricky part is handling faults.

--
This space intentionally left non-blank.

Jay Sekora

unread,
Aug 21, 1997, 3:00:00 AM8/21/97
to

Cameron Laird <cla...@Starbase.NeoSoft.COM> wrote:
Kudos to Jay for making this generally available.

Thanks!

I'm a strong believer of including a well-chosen example
or two in in-line comments.

Good point. Especially important for this procedure, since (due to
the interactions at three different levels in the procedure-call stack)
it's a bit hard to get one's head around. In my defense, though,
there's a lot of fairly elaborate documentation installed as part
of the full package.

j.

PS - I've stolen your example for the code. :-)

0 new messages