Version 11 of init

Updated 2010-12-27 22:52:17 by RLE

if 0 {

Larry Smith - This page supersedes getparams. It is the latest and most elegant version of my named parameter parsing routine. It takes an option -using <overrides> parameter, and a list of vars and default values. First it initializes the variables to their given defaults in the calling scope, then it parses the overrides argument and sets those "-var val" pairs in the calling scope. In essence, it declares all your local variables and then allows them to be set by command-line switches. But it will not permit vars mentioned in the overrides being set unless they appear in the vars list. This prevents the caller from setting local or meaningless variables.

Init returns a list of -var val pairs for all the vars it did not recognize. This allows you to pass through things for later processing.

 }

 # init.tcl
 # Copyright 2001,2005 by Larry Smith
 # Wild Open Source, Inc
 # For license terms see "COPYING"
 #
 # Takes a list of variable-value pairs and creates and
 # initializes the former with the latter in the calling
 # context.  If the first parameter is "-using" it will
 # same away the following argument and use it in a second
 # pass to alter values set in the first pass.  In will not
 # create or modify vars not mentioned in the initializing
 # list.  Returns a list of unrecognized var val pairs.
 # If a -var is followed by another -var or by the end of
 # the list, the var is set to 1.

 proc init { args } {
   if { [ llength $args ] == 0 } return
   if { [ llength $args ] == 1 } { eval set args $args }
   set arglist {}
   if { [ string equal -using [ lindex $args 0 ] ] } {
     set arglist [ lindex $args 1 ]; set args [ lrange $args 2 end ]
   }
   set varlist {}
   set rest {}
   # first pass - args on command line
   foreach { var val } $args {
     uplevel 1 set $var \{$val\}
     lappend varlist $var
   }
   # second pass - args passed in -using list
   set len [ llength $arglist ]
   for { set i 0 } { $i < $len } { incr i } {
     set var [ string range [ lindex $arglist $i ] 1 end ]
     incr i
     if { $i < [ llength $arglist ] } {
       set val [ lindex $arglist $i ]
     } else {
       set val 1
     }
     if { [ string index $val 0 ] == "-" } {
       incr i -1
       set val 1
     }
     if { [ lsearch $varlist $var ] != -1 } {
       uplevel 1 set $var \{$val\}
     } else {
       lappend rest -$var $val
     }
   }
   return $rest
 }

if 0 { Examples:

 proc foo { args } {
   init -using $args a 1 b 2 c 3
   puts "$a, $b, $c"
 }

 foo -a 3

prints "3, 2, 3"

 foo -b 1 -c 4

prints "1, 1, 4"

 foo -b 1 -d 5

prints "1, 1, 3" and the attempt to set a var "d" is ignored (it did not appear in the init list).

To parse your script parameters you can do: init -using $argv global1 value1 global2 value2

You can also brace arguments:

 foo -a 5 -b 2

can also be expressed as:

 foo {
   -a 5
   -b 2
 }

To init without command-line parsing:

  init a 1 b 2 c 3

will simply set these local (or global if in the #0 scope) vars.

The -using parameter is usually $args but as noted above it could also be $argv, or something from a database - anything that can be allowed to override the usual defaults in the init list.

2010-12-27 RLE:

To get this to work as advertised with 8.5.8 I had to make the following change:

--- init.orig 2010-12-27 17:47:42.217064828 -0500 +++ init.fixed 2010-12-27 17:48:28.246065737 -0500 @@ -15,10 +15,10 @@

 proc init { args } {
   if { [ llength $args ] == 0 } return

- if { llength $args == 1 } { eval set args $args }

   set arglist {}
   if { [ string equal -using [ lindex $args 0 ] ] } {
     set arglist [ lindex $args 1 ]; set args [ lrange $args 2 end ]

+ if { llength $arglist == 1 } { set arglist {*}$arglist }

   }
   set varlist {}
   set rest {}

}


Category Argument Processing