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 http://starbase.neosoft.com/~claird/home.html
cla...@NeoSoft.com +1 713 996 8546 FAX
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 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)
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/
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
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.
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.
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. :-)