annotate CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/tcl8.6/opt0.4/optparse.tcl @ 68:5028fdace37b

planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author jpayne
date Tue, 18 Mar 2025 16:23:26 -0400
parents
children
rev   line source
jpayne@68 1 # optparse.tcl --
jpayne@68 2 #
jpayne@68 3 # (private) Option parsing package
jpayne@68 4 # Primarily used internally by the safe:: code.
jpayne@68 5 #
jpayne@68 6 # WARNING: This code will go away in a future release
jpayne@68 7 # of Tcl. It is NOT supported and you should not rely
jpayne@68 8 # on it. If your code does rely on this package you
jpayne@68 9 # may directly incorporate this code into your application.
jpayne@68 10
jpayne@68 11 package require Tcl 8.5-
jpayne@68 12 # When this version number changes, update the pkgIndex.tcl file
jpayne@68 13 # and the install directory in the Makefiles.
jpayne@68 14 package provide opt 0.4.8
jpayne@68 15
jpayne@68 16 namespace eval ::tcl {
jpayne@68 17
jpayne@68 18 # Exported APIs
jpayne@68 19 namespace export OptKeyRegister OptKeyDelete OptKeyError OptKeyParse \
jpayne@68 20 OptProc OptProcArgGiven OptParse \
jpayne@68 21 Lempty Lget \
jpayne@68 22 Lassign Lvarpop Lvarpop1 Lvarset Lvarincr \
jpayne@68 23 SetMax SetMin
jpayne@68 24
jpayne@68 25
jpayne@68 26 ################# Example of use / 'user documentation' ###################
jpayne@68 27
jpayne@68 28 proc OptCreateTestProc {} {
jpayne@68 29
jpayne@68 30 # Defines ::tcl::OptParseTest as a test proc with parsed arguments
jpayne@68 31 # (can't be defined before the code below is loaded (before "OptProc"))
jpayne@68 32
jpayne@68 33 # Every OptProc give usage information on "procname -help".
jpayne@68 34 # Try "tcl::OptParseTest -help" and "tcl::OptParseTest -a" and
jpayne@68 35 # then other arguments.
jpayne@68 36 #
jpayne@68 37 # example of 'valid' call:
jpayne@68 38 # ::tcl::OptParseTest save -4 -pr 23 -libsok SybTcl\
jpayne@68 39 # -nostatics false ch1
jpayne@68 40 OptProc OptParseTest {
jpayne@68 41 {subcommand -choice {save print} "sub command"}
jpayne@68 42 {arg1 3 "some number"}
jpayne@68 43 {-aflag}
jpayne@68 44 {-intflag 7}
jpayne@68 45 {-weirdflag "help string"}
jpayne@68 46 {-noStatics "Not ok to load static packages"}
jpayne@68 47 {-nestedloading1 true "OK to load into nested children"}
jpayne@68 48 {-nestedloading2 -boolean true "OK to load into nested children"}
jpayne@68 49 {-libsOK -choice {Tk SybTcl}
jpayne@68 50 "List of packages that can be loaded"}
jpayne@68 51 {-precision -int 12 "Number of digits of precision"}
jpayne@68 52 {-intval 7 "An integer"}
jpayne@68 53 {-scale -float 1.0 "Scale factor"}
jpayne@68 54 {-zoom 1.0 "Zoom factor"}
jpayne@68 55 {-arbitrary foobar "Arbitrary string"}
jpayne@68 56 {-random -string 12 "Random string"}
jpayne@68 57 {-listval -list {} "List value"}
jpayne@68 58 {-blahflag -blah abc "Funny type"}
jpayne@68 59 {arg2 -boolean "a boolean"}
jpayne@68 60 {arg3 -choice "ch1 ch2"}
jpayne@68 61 {?optarg? -list {} "optional argument"}
jpayne@68 62 } {
jpayne@68 63 foreach v [info locals] {
jpayne@68 64 puts stderr [format "%14s : %s" $v [set $v]]
jpayne@68 65 }
jpayne@68 66 }
jpayne@68 67 }
jpayne@68 68
jpayne@68 69 ################### No User serviceable part below ! ###############
jpayne@68 70
jpayne@68 71 # Array storing the parsed descriptions
jpayne@68 72 variable OptDesc
jpayne@68 73 array set OptDesc {}
jpayne@68 74 # Next potentially free key id (numeric)
jpayne@68 75 variable OptDescN 0
jpayne@68 76
jpayne@68 77 # Inside algorithm/mechanism description:
jpayne@68 78 # (not for the faint hearted ;-)
jpayne@68 79 #
jpayne@68 80 # The argument description is parsed into a "program tree"
jpayne@68 81 # It is called a "program" because it is the program used by
jpayne@68 82 # the state machine interpreter that use that program to
jpayne@68 83 # actually parse the arguments at run time.
jpayne@68 84 #
jpayne@68 85 # The general structure of a "program" is
jpayne@68 86 # notation (pseudo bnf like)
jpayne@68 87 # name :== definition defines "name" as being "definition"
jpayne@68 88 # { x y z } means list of x, y, and z
jpayne@68 89 # x* means x repeated 0 or more time
jpayne@68 90 # x+ means "x x*"
jpayne@68 91 # x? means optionally x
jpayne@68 92 # x | y means x or y
jpayne@68 93 # "cccc" means the literal string
jpayne@68 94 #
jpayne@68 95 # program :== { programCounter programStep* }
jpayne@68 96 #
jpayne@68 97 # programStep :== program | singleStep
jpayne@68 98 #
jpayne@68 99 # programCounter :== {"P" integer+ }
jpayne@68 100 #
jpayne@68 101 # singleStep :== { instruction parameters* }
jpayne@68 102 #
jpayne@68 103 # instruction :== single element list
jpayne@68 104 #
jpayne@68 105 # (the difference between singleStep and program is that \
jpayne@68 106 # llength [lindex $program 0] >= 2
jpayne@68 107 # while
jpayne@68 108 # llength [lindex $singleStep 0] == 1
jpayne@68 109 # )
jpayne@68 110 #
jpayne@68 111 # And for this application:
jpayne@68 112 #
jpayne@68 113 # singleStep :== { instruction varname {hasBeenSet currentValue} type
jpayne@68 114 # typeArgs help }
jpayne@68 115 # instruction :== "flags" | "value"
jpayne@68 116 # type :== knowType | anyword
jpayne@68 117 # knowType :== "string" | "int" | "boolean" | "boolflag" | "float"
jpayne@68 118 # | "choice"
jpayne@68 119 #
jpayne@68 120 # for type "choice" typeArgs is a list of possible choices, the first one
jpayne@68 121 # is the default value. for all other types the typeArgs is the default value
jpayne@68 122 #
jpayne@68 123 # a "boolflag" is the type for a flag whose presence or absence, without
jpayne@68 124 # additional arguments means respectively true or false (default flag type).
jpayne@68 125 #
jpayne@68 126 # programCounter is the index in the list of the currently processed
jpayne@68 127 # programStep (thus starting at 1 (0 is {"P" prgCounterValue}).
jpayne@68 128 # If it is a list it points toward each currently selected programStep.
jpayne@68 129 # (like for "flags", as they are optional, form a set and programStep).
jpayne@68 130
jpayne@68 131 # Performance/Implementation issues
jpayne@68 132 # ---------------------------------
jpayne@68 133 # We use tcl lists instead of arrays because with tcl8.0
jpayne@68 134 # they should start to be much faster.
jpayne@68 135 # But this code use a lot of helper procs (like Lvarset)
jpayne@68 136 # which are quite slow and would be helpfully optimized
jpayne@68 137 # for instance by being written in C. Also our struture
jpayne@68 138 # is complex and there is maybe some places where the
jpayne@68 139 # string rep might be calculated at great exense. to be checked.
jpayne@68 140
jpayne@68 141 #
jpayne@68 142 # Parse a given description and saves it here under the given key
jpayne@68 143 # generate a unused keyid if not given
jpayne@68 144 #
jpayne@68 145 proc ::tcl::OptKeyRegister {desc {key ""}} {
jpayne@68 146 variable OptDesc
jpayne@68 147 variable OptDescN
jpayne@68 148 if {[string equal $key ""]} {
jpayne@68 149 # in case a key given to us as a parameter was a number
jpayne@68 150 while {[info exists OptDesc($OptDescN)]} {incr OptDescN}
jpayne@68 151 set key $OptDescN
jpayne@68 152 incr OptDescN
jpayne@68 153 }
jpayne@68 154 # program counter
jpayne@68 155 set program [list [list "P" 1]]
jpayne@68 156
jpayne@68 157 # are we processing flags (which makes a single program step)
jpayne@68 158 set inflags 0
jpayne@68 159
jpayne@68 160 set state {}
jpayne@68 161
jpayne@68 162 # flag used to detect that we just have a single (flags set) subprogram.
jpayne@68 163 set empty 1
jpayne@68 164
jpayne@68 165 foreach item $desc {
jpayne@68 166 if {$state == "args"} {
jpayne@68 167 # more items after 'args'...
jpayne@68 168 return -code error "'args' special argument must be the last one"
jpayne@68 169 }
jpayne@68 170 set res [OptNormalizeOne $item]
jpayne@68 171 set state [lindex $res 0]
jpayne@68 172 if {$inflags} {
jpayne@68 173 if {$state == "flags"} {
jpayne@68 174 # add to 'subprogram'
jpayne@68 175 lappend flagsprg $res
jpayne@68 176 } else {
jpayne@68 177 # put in the flags
jpayne@68 178 # structure for flag programs items is a list of
jpayne@68 179 # {subprgcounter {prg flag 1} {prg flag 2} {...}}
jpayne@68 180 lappend program $flagsprg
jpayne@68 181 # put the other regular stuff
jpayne@68 182 lappend program $res
jpayne@68 183 set inflags 0
jpayne@68 184 set empty 0
jpayne@68 185 }
jpayne@68 186 } else {
jpayne@68 187 if {$state == "flags"} {
jpayne@68 188 set inflags 1
jpayne@68 189 # sub program counter + first sub program
jpayne@68 190 set flagsprg [list [list "P" 1] $res]
jpayne@68 191 } else {
jpayne@68 192 lappend program $res
jpayne@68 193 set empty 0
jpayne@68 194 }
jpayne@68 195 }
jpayne@68 196 }
jpayne@68 197 if {$inflags} {
jpayne@68 198 if {$empty} {
jpayne@68 199 # We just have the subprogram, optimize and remove
jpayne@68 200 # unneeded level:
jpayne@68 201 set program $flagsprg
jpayne@68 202 } else {
jpayne@68 203 lappend program $flagsprg
jpayne@68 204 }
jpayne@68 205 }
jpayne@68 206
jpayne@68 207 set OptDesc($key) $program
jpayne@68 208
jpayne@68 209 return $key
jpayne@68 210 }
jpayne@68 211
jpayne@68 212 #
jpayne@68 213 # Free the storage for that given key
jpayne@68 214 #
jpayne@68 215 proc ::tcl::OptKeyDelete {key} {
jpayne@68 216 variable OptDesc
jpayne@68 217 unset OptDesc($key)
jpayne@68 218 }
jpayne@68 219
jpayne@68 220 # Get the parsed description stored under the given key.
jpayne@68 221 proc OptKeyGetDesc {descKey} {
jpayne@68 222 variable OptDesc
jpayne@68 223 if {![info exists OptDesc($descKey)]} {
jpayne@68 224 return -code error "Unknown option description key \"$descKey\""
jpayne@68 225 }
jpayne@68 226 set OptDesc($descKey)
jpayne@68 227 }
jpayne@68 228
jpayne@68 229 # Parse entry point for ppl who don't want to register with a key,
jpayne@68 230 # for instance because the description changes dynamically.
jpayne@68 231 # (otherwise one should really use OptKeyRegister once + OptKeyParse
jpayne@68 232 # as it is way faster or simply OptProc which does it all)
jpayne@68 233 # Assign a temporary key, call OptKeyParse and then free the storage
jpayne@68 234 proc ::tcl::OptParse {desc arglist} {
jpayne@68 235 set tempkey [OptKeyRegister $desc]
jpayne@68 236 set ret [catch {uplevel 1 [list ::tcl::OptKeyParse $tempkey $arglist]} res]
jpayne@68 237 OptKeyDelete $tempkey
jpayne@68 238 return -code $ret $res
jpayne@68 239 }
jpayne@68 240
jpayne@68 241 # Helper function, replacement for proc that both
jpayne@68 242 # register the description under a key which is the name of the proc
jpayne@68 243 # (and thus unique to that code)
jpayne@68 244 # and add a first line to the code to call the OptKeyParse proc
jpayne@68 245 # Stores the list of variables that have been actually given by the user
jpayne@68 246 # (the other will be sets to their default value)
jpayne@68 247 # into local variable named "Args".
jpayne@68 248 proc ::tcl::OptProc {name desc body} {
jpayne@68 249 set namespace [uplevel 1 [list ::namespace current]]
jpayne@68 250 if {[string match "::*" $name] || [string equal $namespace "::"]} {
jpayne@68 251 # absolute name or global namespace, name is the key
jpayne@68 252 set key $name
jpayne@68 253 } else {
jpayne@68 254 # we are relative to some non top level namespace:
jpayne@68 255 set key "${namespace}::${name}"
jpayne@68 256 }
jpayne@68 257 OptKeyRegister $desc $key
jpayne@68 258 uplevel 1 [list ::proc $name args "set Args \[::tcl::OptKeyParse $key \$args\]\n$body"]
jpayne@68 259 return $key
jpayne@68 260 }
jpayne@68 261 # Check that a argument has been given
jpayne@68 262 # assumes that "OptProc" has been used as it will check in "Args" list
jpayne@68 263 proc ::tcl::OptProcArgGiven {argname} {
jpayne@68 264 upvar Args alist
jpayne@68 265 expr {[lsearch $alist $argname] >=0}
jpayne@68 266 }
jpayne@68 267
jpayne@68 268 #######
jpayne@68 269 # Programs/Descriptions manipulation
jpayne@68 270
jpayne@68 271 # Return the instruction word/list of a given step/(sub)program
jpayne@68 272 proc OptInstr {lst} {
jpayne@68 273 lindex $lst 0
jpayne@68 274 }
jpayne@68 275 # Is a (sub) program or a plain instruction ?
jpayne@68 276 proc OptIsPrg {lst} {
jpayne@68 277 expr {[llength [OptInstr $lst]]>=2}
jpayne@68 278 }
jpayne@68 279 # Is this instruction a program counter or a real instr
jpayne@68 280 proc OptIsCounter {item} {
jpayne@68 281 expr {[lindex $item 0]=="P"}
jpayne@68 282 }
jpayne@68 283 # Current program counter (2nd word of first word)
jpayne@68 284 proc OptGetPrgCounter {lst} {
jpayne@68 285 Lget $lst {0 1}
jpayne@68 286 }
jpayne@68 287 # Current program counter (2nd word of first word)
jpayne@68 288 proc OptSetPrgCounter {lstName newValue} {
jpayne@68 289 upvar $lstName lst
jpayne@68 290 set lst [lreplace $lst 0 0 [concat "P" $newValue]]
jpayne@68 291 }
jpayne@68 292 # returns a list of currently selected items.
jpayne@68 293 proc OptSelection {lst} {
jpayne@68 294 set res {}
jpayne@68 295 foreach idx [lrange [lindex $lst 0] 1 end] {
jpayne@68 296 lappend res [Lget $lst $idx]
jpayne@68 297 }
jpayne@68 298 return $res
jpayne@68 299 }
jpayne@68 300
jpayne@68 301 # Advance to next description
jpayne@68 302 proc OptNextDesc {descName} {
jpayne@68 303 uplevel 1 [list Lvarincr $descName {0 1}]
jpayne@68 304 }
jpayne@68 305
jpayne@68 306 # Get the current description, eventually descend
jpayne@68 307 proc OptCurDesc {descriptions} {
jpayne@68 308 lindex $descriptions [OptGetPrgCounter $descriptions]
jpayne@68 309 }
jpayne@68 310 # get the current description, eventually descend
jpayne@68 311 # through sub programs as needed.
jpayne@68 312 proc OptCurDescFinal {descriptions} {
jpayne@68 313 set item [OptCurDesc $descriptions]
jpayne@68 314 # Descend untill we get the actual item and not a sub program
jpayne@68 315 while {[OptIsPrg $item]} {
jpayne@68 316 set item [OptCurDesc $item]
jpayne@68 317 }
jpayne@68 318 return $item
jpayne@68 319 }
jpayne@68 320 # Current final instruction adress
jpayne@68 321 proc OptCurAddr {descriptions {start {}}} {
jpayne@68 322 set adress [OptGetPrgCounter $descriptions]
jpayne@68 323 lappend start $adress
jpayne@68 324 set item [lindex $descriptions $adress]
jpayne@68 325 if {[OptIsPrg $item]} {
jpayne@68 326 return [OptCurAddr $item $start]
jpayne@68 327 } else {
jpayne@68 328 return $start
jpayne@68 329 }
jpayne@68 330 }
jpayne@68 331 # Set the value field of the current instruction
jpayne@68 332 proc OptCurSetValue {descriptionsName value} {
jpayne@68 333 upvar $descriptionsName descriptions
jpayne@68 334 # get the current item full adress
jpayne@68 335 set adress [OptCurAddr $descriptions]
jpayne@68 336 # use the 3th field of the item (see OptValue / OptNewInst)
jpayne@68 337 lappend adress 2
jpayne@68 338 Lvarset descriptions $adress [list 1 $value]
jpayne@68 339 # ^hasBeenSet flag
jpayne@68 340 }
jpayne@68 341
jpayne@68 342 # empty state means done/paste the end of the program
jpayne@68 343 proc OptState {item} {
jpayne@68 344 lindex $item 0
jpayne@68 345 }
jpayne@68 346
jpayne@68 347 # current state
jpayne@68 348 proc OptCurState {descriptions} {
jpayne@68 349 OptState [OptCurDesc $descriptions]
jpayne@68 350 }
jpayne@68 351
jpayne@68 352 #######
jpayne@68 353 # Arguments manipulation
jpayne@68 354
jpayne@68 355 # Returns the argument that has to be processed now
jpayne@68 356 proc OptCurrentArg {lst} {
jpayne@68 357 lindex $lst 0
jpayne@68 358 }
jpayne@68 359 # Advance to next argument
jpayne@68 360 proc OptNextArg {argsName} {
jpayne@68 361 uplevel 1 [list Lvarpop1 $argsName]
jpayne@68 362 }
jpayne@68 363 #######
jpayne@68 364
jpayne@68 365
jpayne@68 366
jpayne@68 367
jpayne@68 368
jpayne@68 369 # Loop over all descriptions, calling OptDoOne which will
jpayne@68 370 # eventually eat all the arguments.
jpayne@68 371 proc OptDoAll {descriptionsName argumentsName} {
jpayne@68 372 upvar $descriptionsName descriptions
jpayne@68 373 upvar $argumentsName arguments
jpayne@68 374 # puts "entered DoAll"
jpayne@68 375 # Nb: the places where "state" can be set are tricky to figure
jpayne@68 376 # because DoOne sets the state to flagsValue and return -continue
jpayne@68 377 # when needed...
jpayne@68 378 set state [OptCurState $descriptions]
jpayne@68 379 # We'll exit the loop in "OptDoOne" or when state is empty.
jpayne@68 380 while 1 {
jpayne@68 381 set curitem [OptCurDesc $descriptions]
jpayne@68 382 # Do subprograms if needed, call ourselves on the sub branch
jpayne@68 383 while {[OptIsPrg $curitem]} {
jpayne@68 384 OptDoAll curitem arguments
jpayne@68 385 # puts "done DoAll sub"
jpayne@68 386 # Insert back the results in current tree
jpayne@68 387 Lvarset1nc descriptions [OptGetPrgCounter $descriptions]\
jpayne@68 388 $curitem
jpayne@68 389 OptNextDesc descriptions
jpayne@68 390 set curitem [OptCurDesc $descriptions]
jpayne@68 391 set state [OptCurState $descriptions]
jpayne@68 392 }
jpayne@68 393 # puts "state = \"$state\" - arguments=($arguments)"
jpayne@68 394 if {[Lempty $state]} {
jpayne@68 395 # Nothing left to do, we are done in this branch:
jpayne@68 396 break
jpayne@68 397 }
jpayne@68 398 # The following statement can make us terminate/continue
jpayne@68 399 # as it use return -code {break, continue, return and error}
jpayne@68 400 # codes
jpayne@68 401 OptDoOne descriptions state arguments
jpayne@68 402 # If we are here, no special return code where issued,
jpayne@68 403 # we'll step to next instruction :
jpayne@68 404 # puts "new state = \"$state\""
jpayne@68 405 OptNextDesc descriptions
jpayne@68 406 set state [OptCurState $descriptions]
jpayne@68 407 }
jpayne@68 408 }
jpayne@68 409
jpayne@68 410 # Process one step for the state machine,
jpayne@68 411 # eventually consuming the current argument.
jpayne@68 412 proc OptDoOne {descriptionsName stateName argumentsName} {
jpayne@68 413 upvar $argumentsName arguments
jpayne@68 414 upvar $descriptionsName descriptions
jpayne@68 415 upvar $stateName state
jpayne@68 416
jpayne@68 417 # the special state/instruction "args" eats all
jpayne@68 418 # the remaining args (if any)
jpayne@68 419 if {($state == "args")} {
jpayne@68 420 if {![Lempty $arguments]} {
jpayne@68 421 # If there is no additional arguments, leave the default value
jpayne@68 422 # in.
jpayne@68 423 OptCurSetValue descriptions $arguments
jpayne@68 424 set arguments {}
jpayne@68 425 }
jpayne@68 426 # puts "breaking out ('args' state: consuming every reminding args)"
jpayne@68 427 return -code break
jpayne@68 428 }
jpayne@68 429
jpayne@68 430 if {[Lempty $arguments]} {
jpayne@68 431 if {$state == "flags"} {
jpayne@68 432 # no argument and no flags : we're done
jpayne@68 433 # puts "returning to previous (sub)prg (no more args)"
jpayne@68 434 return -code return
jpayne@68 435 } elseif {$state == "optValue"} {
jpayne@68 436 set state next; # not used, for debug only
jpayne@68 437 # go to next state
jpayne@68 438 return
jpayne@68 439 } else {
jpayne@68 440 return -code error [OptMissingValue $descriptions]
jpayne@68 441 }
jpayne@68 442 } else {
jpayne@68 443 set arg [OptCurrentArg $arguments]
jpayne@68 444 }
jpayne@68 445
jpayne@68 446 switch $state {
jpayne@68 447 flags {
jpayne@68 448 # A non-dash argument terminates the options, as does --
jpayne@68 449
jpayne@68 450 # Still a flag ?
jpayne@68 451 if {![OptIsFlag $arg]} {
jpayne@68 452 # don't consume the argument, return to previous prg
jpayne@68 453 return -code return
jpayne@68 454 }
jpayne@68 455 # consume the flag
jpayne@68 456 OptNextArg arguments
jpayne@68 457 if {[string equal "--" $arg]} {
jpayne@68 458 # return from 'flags' state
jpayne@68 459 return -code return
jpayne@68 460 }
jpayne@68 461
jpayne@68 462 set hits [OptHits descriptions $arg]
jpayne@68 463 if {$hits > 1} {
jpayne@68 464 return -code error [OptAmbigous $descriptions $arg]
jpayne@68 465 } elseif {$hits == 0} {
jpayne@68 466 return -code error [OptFlagUsage $descriptions $arg]
jpayne@68 467 }
jpayne@68 468 set item [OptCurDesc $descriptions]
jpayne@68 469 if {[OptNeedValue $item]} {
jpayne@68 470 # we need a value, next state is
jpayne@68 471 set state flagValue
jpayne@68 472 } else {
jpayne@68 473 OptCurSetValue descriptions 1
jpayne@68 474 }
jpayne@68 475 # continue
jpayne@68 476 return -code continue
jpayne@68 477 }
jpayne@68 478 flagValue -
jpayne@68 479 value {
jpayne@68 480 set item [OptCurDesc $descriptions]
jpayne@68 481 # Test the values against their required type
jpayne@68 482 if {[catch {OptCheckType $arg\
jpayne@68 483 [OptType $item] [OptTypeArgs $item]} val]} {
jpayne@68 484 return -code error [OptBadValue $item $arg $val]
jpayne@68 485 }
jpayne@68 486 # consume the value
jpayne@68 487 OptNextArg arguments
jpayne@68 488 # set the value
jpayne@68 489 OptCurSetValue descriptions $val
jpayne@68 490 # go to next state
jpayne@68 491 if {$state == "flagValue"} {
jpayne@68 492 set state flags
jpayne@68 493 return -code continue
jpayne@68 494 } else {
jpayne@68 495 set state next; # not used, for debug only
jpayne@68 496 return ; # will go on next step
jpayne@68 497 }
jpayne@68 498 }
jpayne@68 499 optValue {
jpayne@68 500 set item [OptCurDesc $descriptions]
jpayne@68 501 # Test the values against their required type
jpayne@68 502 if {![catch {OptCheckType $arg\
jpayne@68 503 [OptType $item] [OptTypeArgs $item]} val]} {
jpayne@68 504 # right type, so :
jpayne@68 505 # consume the value
jpayne@68 506 OptNextArg arguments
jpayne@68 507 # set the value
jpayne@68 508 OptCurSetValue descriptions $val
jpayne@68 509 }
jpayne@68 510 # go to next state
jpayne@68 511 set state next; # not used, for debug only
jpayne@68 512 return ; # will go on next step
jpayne@68 513 }
jpayne@68 514 }
jpayne@68 515 # If we reach this point: an unknown
jpayne@68 516 # state as been entered !
jpayne@68 517 return -code error "Bug! unknown state in DoOne \"$state\"\
jpayne@68 518 (prg counter [OptGetPrgCounter $descriptions]:\
jpayne@68 519 [OptCurDesc $descriptions])"
jpayne@68 520 }
jpayne@68 521
jpayne@68 522 # Parse the options given the key to previously registered description
jpayne@68 523 # and arguments list
jpayne@68 524 proc ::tcl::OptKeyParse {descKey arglist} {
jpayne@68 525
jpayne@68 526 set desc [OptKeyGetDesc $descKey]
jpayne@68 527
jpayne@68 528 # make sure -help always give usage
jpayne@68 529 if {[string equal -nocase "-help" $arglist]} {
jpayne@68 530 return -code error [OptError "Usage information:" $desc 1]
jpayne@68 531 }
jpayne@68 532
jpayne@68 533 OptDoAll desc arglist
jpayne@68 534
jpayne@68 535 if {![Lempty $arglist]} {
jpayne@68 536 return -code error [OptTooManyArgs $desc $arglist]
jpayne@68 537 }
jpayne@68 538
jpayne@68 539 # Analyse the result
jpayne@68 540 # Walk through the tree:
jpayne@68 541 OptTreeVars $desc "#[expr {[info level]-1}]"
jpayne@68 542 }
jpayne@68 543
jpayne@68 544 # determine string length for nice tabulated output
jpayne@68 545 proc OptTreeVars {desc level {vnamesLst {}}} {
jpayne@68 546 foreach item $desc {
jpayne@68 547 if {[OptIsCounter $item]} continue
jpayne@68 548 if {[OptIsPrg $item]} {
jpayne@68 549 set vnamesLst [OptTreeVars $item $level $vnamesLst]
jpayne@68 550 } else {
jpayne@68 551 set vname [OptVarName $item]
jpayne@68 552 upvar $level $vname var
jpayne@68 553 if {[OptHasBeenSet $item]} {
jpayne@68 554 # puts "adding $vname"
jpayne@68 555 # lets use the input name for the returned list
jpayne@68 556 # it is more usefull, for instance you can check that
jpayne@68 557 # no flags at all was given with expr
jpayne@68 558 # {![string match "*-*" $Args]}
jpayne@68 559 lappend vnamesLst [OptName $item]
jpayne@68 560 set var [OptValue $item]
jpayne@68 561 } else {
jpayne@68 562 set var [OptDefaultValue $item]
jpayne@68 563 }
jpayne@68 564 }
jpayne@68 565 }
jpayne@68 566 return $vnamesLst
jpayne@68 567 }
jpayne@68 568
jpayne@68 569
jpayne@68 570 # Check the type of a value
jpayne@68 571 # and emit an error if arg is not of the correct type
jpayne@68 572 # otherwise returns the canonical value of that arg (ie 0/1 for booleans)
jpayne@68 573 proc ::tcl::OptCheckType {arg type {typeArgs ""}} {
jpayne@68 574 # puts "checking '$arg' against '$type' ($typeArgs)"
jpayne@68 575
jpayne@68 576 # only types "any", "choice", and numbers can have leading "-"
jpayne@68 577
jpayne@68 578 switch -exact -- $type {
jpayne@68 579 int {
jpayne@68 580 if {![string is integer -strict $arg]} {
jpayne@68 581 error "not an integer"
jpayne@68 582 }
jpayne@68 583 return $arg
jpayne@68 584 }
jpayne@68 585 float {
jpayne@68 586 return [expr {double($arg)}]
jpayne@68 587 }
jpayne@68 588 script -
jpayne@68 589 list {
jpayne@68 590 # if llength fail : malformed list
jpayne@68 591 if {[llength $arg]==0 && [OptIsFlag $arg]} {
jpayne@68 592 error "no values with leading -"
jpayne@68 593 }
jpayne@68 594 return $arg
jpayne@68 595 }
jpayne@68 596 boolean {
jpayne@68 597 if {![string is boolean -strict $arg]} {
jpayne@68 598 error "non canonic boolean"
jpayne@68 599 }
jpayne@68 600 # convert true/false because expr/if is broken with "!,...
jpayne@68 601 return [expr {$arg ? 1 : 0}]
jpayne@68 602 }
jpayne@68 603 choice {
jpayne@68 604 if {$arg ni $typeArgs} {
jpayne@68 605 error "invalid choice"
jpayne@68 606 }
jpayne@68 607 return $arg
jpayne@68 608 }
jpayne@68 609 any {
jpayne@68 610 return $arg
jpayne@68 611 }
jpayne@68 612 string -
jpayne@68 613 default {
jpayne@68 614 if {[OptIsFlag $arg]} {
jpayne@68 615 error "no values with leading -"
jpayne@68 616 }
jpayne@68 617 return $arg
jpayne@68 618 }
jpayne@68 619 }
jpayne@68 620 return neverReached
jpayne@68 621 }
jpayne@68 622
jpayne@68 623 # internal utilities
jpayne@68 624
jpayne@68 625 # returns the number of flags matching the given arg
jpayne@68 626 # sets the (local) prg counter to the list of matches
jpayne@68 627 proc OptHits {descName arg} {
jpayne@68 628 upvar $descName desc
jpayne@68 629 set hits 0
jpayne@68 630 set hitems {}
jpayne@68 631 set i 1
jpayne@68 632
jpayne@68 633 set larg [string tolower $arg]
jpayne@68 634 set len [string length $larg]
jpayne@68 635 set last [expr {$len-1}]
jpayne@68 636
jpayne@68 637 foreach item [lrange $desc 1 end] {
jpayne@68 638 set flag [OptName $item]
jpayne@68 639 # lets try to match case insensitively
jpayne@68 640 # (string length ought to be cheap)
jpayne@68 641 set lflag [string tolower $flag]
jpayne@68 642 if {$len == [string length $lflag]} {
jpayne@68 643 if {[string equal $larg $lflag]} {
jpayne@68 644 # Exact match case
jpayne@68 645 OptSetPrgCounter desc $i
jpayne@68 646 return 1
jpayne@68 647 }
jpayne@68 648 } elseif {[string equal $larg [string range $lflag 0 $last]]} {
jpayne@68 649 lappend hitems $i
jpayne@68 650 incr hits
jpayne@68 651 }
jpayne@68 652 incr i
jpayne@68 653 }
jpayne@68 654 if {$hits} {
jpayne@68 655 OptSetPrgCounter desc $hitems
jpayne@68 656 }
jpayne@68 657 return $hits
jpayne@68 658 }
jpayne@68 659
jpayne@68 660 # Extract fields from the list structure:
jpayne@68 661
jpayne@68 662 proc OptName {item} {
jpayne@68 663 lindex $item 1
jpayne@68 664 }
jpayne@68 665 proc OptHasBeenSet {item} {
jpayne@68 666 Lget $item {2 0}
jpayne@68 667 }
jpayne@68 668 proc OptValue {item} {
jpayne@68 669 Lget $item {2 1}
jpayne@68 670 }
jpayne@68 671
jpayne@68 672 proc OptIsFlag {name} {
jpayne@68 673 string match "-*" $name
jpayne@68 674 }
jpayne@68 675 proc OptIsOpt {name} {
jpayne@68 676 string match {\?*} $name
jpayne@68 677 }
jpayne@68 678 proc OptVarName {item} {
jpayne@68 679 set name [OptName $item]
jpayne@68 680 if {[OptIsFlag $name]} {
jpayne@68 681 return [string range $name 1 end]
jpayne@68 682 } elseif {[OptIsOpt $name]} {
jpayne@68 683 return [string trim $name "?"]
jpayne@68 684 } else {
jpayne@68 685 return $name
jpayne@68 686 }
jpayne@68 687 }
jpayne@68 688 proc OptType {item} {
jpayne@68 689 lindex $item 3
jpayne@68 690 }
jpayne@68 691 proc OptTypeArgs {item} {
jpayne@68 692 lindex $item 4
jpayne@68 693 }
jpayne@68 694 proc OptHelp {item} {
jpayne@68 695 lindex $item 5
jpayne@68 696 }
jpayne@68 697 proc OptNeedValue {item} {
jpayne@68 698 expr {![string equal [OptType $item] boolflag]}
jpayne@68 699 }
jpayne@68 700 proc OptDefaultValue {item} {
jpayne@68 701 set val [OptTypeArgs $item]
jpayne@68 702 switch -exact -- [OptType $item] {
jpayne@68 703 choice {return [lindex $val 0]}
jpayne@68 704 boolean -
jpayne@68 705 boolflag {
jpayne@68 706 # convert back false/true to 0/1 because expr !$bool
jpayne@68 707 # is broken..
jpayne@68 708 if {$val} {
jpayne@68 709 return 1
jpayne@68 710 } else {
jpayne@68 711 return 0
jpayne@68 712 }
jpayne@68 713 }
jpayne@68 714 }
jpayne@68 715 return $val
jpayne@68 716 }
jpayne@68 717
jpayne@68 718 # Description format error helper
jpayne@68 719 proc OptOptUsage {item {what ""}} {
jpayne@68 720 return -code error "invalid description format$what: $item\n\
jpayne@68 721 should be a list of {varname|-flagname ?-type? ?defaultvalue?\
jpayne@68 722 ?helpstring?}"
jpayne@68 723 }
jpayne@68 724
jpayne@68 725
jpayne@68 726 # Generate a canonical form single instruction
jpayne@68 727 proc OptNewInst {state varname type typeArgs help} {
jpayne@68 728 list $state $varname [list 0 {}] $type $typeArgs $help
jpayne@68 729 # ^ ^
jpayne@68 730 # | |
jpayne@68 731 # hasBeenSet=+ +=currentValue
jpayne@68 732 }
jpayne@68 733
jpayne@68 734 # Translate one item to canonical form
jpayne@68 735 proc OptNormalizeOne {item} {
jpayne@68 736 set lg [Lassign $item varname arg1 arg2 arg3]
jpayne@68 737 # puts "called optnormalizeone '$item' v=($varname), lg=$lg"
jpayne@68 738 set isflag [OptIsFlag $varname]
jpayne@68 739 set isopt [OptIsOpt $varname]
jpayne@68 740 if {$isflag} {
jpayne@68 741 set state "flags"
jpayne@68 742 } elseif {$isopt} {
jpayne@68 743 set state "optValue"
jpayne@68 744 } elseif {![string equal $varname "args"]} {
jpayne@68 745 set state "value"
jpayne@68 746 } else {
jpayne@68 747 set state "args"
jpayne@68 748 }
jpayne@68 749
jpayne@68 750 # apply 'smart' 'fuzzy' logic to try to make
jpayne@68 751 # description writer's life easy, and our's difficult :
jpayne@68 752 # let's guess the missing arguments :-)
jpayne@68 753
jpayne@68 754 switch $lg {
jpayne@68 755 1 {
jpayne@68 756 if {$isflag} {
jpayne@68 757 return [OptNewInst $state $varname boolflag false ""]
jpayne@68 758 } else {
jpayne@68 759 return [OptNewInst $state $varname any "" ""]
jpayne@68 760 }
jpayne@68 761 }
jpayne@68 762 2 {
jpayne@68 763 # varname default
jpayne@68 764 # varname help
jpayne@68 765 set type [OptGuessType $arg1]
jpayne@68 766 if {[string equal $type "string"]} {
jpayne@68 767 if {$isflag} {
jpayne@68 768 set type boolflag
jpayne@68 769 set def false
jpayne@68 770 } else {
jpayne@68 771 set type any
jpayne@68 772 set def ""
jpayne@68 773 }
jpayne@68 774 set help $arg1
jpayne@68 775 } else {
jpayne@68 776 set help ""
jpayne@68 777 set def $arg1
jpayne@68 778 }
jpayne@68 779 return [OptNewInst $state $varname $type $def $help]
jpayne@68 780 }
jpayne@68 781 3 {
jpayne@68 782 # varname type value
jpayne@68 783 # varname value comment
jpayne@68 784
jpayne@68 785 if {[regexp {^-(.+)$} $arg1 x type]} {
jpayne@68 786 # flags/optValue as they are optional, need a "value",
jpayne@68 787 # on the contrary, for a variable (non optional),
jpayne@68 788 # default value is pointless, 'cept for choices :
jpayne@68 789 if {$isflag || $isopt || ($type == "choice")} {
jpayne@68 790 return [OptNewInst $state $varname $type $arg2 ""]
jpayne@68 791 } else {
jpayne@68 792 return [OptNewInst $state $varname $type "" $arg2]
jpayne@68 793 }
jpayne@68 794 } else {
jpayne@68 795 return [OptNewInst $state $varname\
jpayne@68 796 [OptGuessType $arg1] $arg1 $arg2]
jpayne@68 797 }
jpayne@68 798 }
jpayne@68 799 4 {
jpayne@68 800 if {[regexp {^-(.+)$} $arg1 x type]} {
jpayne@68 801 return [OptNewInst $state $varname $type $arg2 $arg3]
jpayne@68 802 } else {
jpayne@68 803 return -code error [OptOptUsage $item]
jpayne@68 804 }
jpayne@68 805 }
jpayne@68 806 default {
jpayne@68 807 return -code error [OptOptUsage $item]
jpayne@68 808 }
jpayne@68 809 }
jpayne@68 810 }
jpayne@68 811
jpayne@68 812 # Auto magic lazy type determination
jpayne@68 813 proc OptGuessType {arg} {
jpayne@68 814 if { $arg == "true" || $arg == "false" } {
jpayne@68 815 return boolean
jpayne@68 816 }
jpayne@68 817 if {[string is integer -strict $arg]} {
jpayne@68 818 return int
jpayne@68 819 }
jpayne@68 820 if {[string is double -strict $arg]} {
jpayne@68 821 return float
jpayne@68 822 }
jpayne@68 823 return string
jpayne@68 824 }
jpayne@68 825
jpayne@68 826 # Error messages front ends
jpayne@68 827
jpayne@68 828 proc OptAmbigous {desc arg} {
jpayne@68 829 OptError "ambigous option \"$arg\", choose from:" [OptSelection $desc]
jpayne@68 830 }
jpayne@68 831 proc OptFlagUsage {desc arg} {
jpayne@68 832 OptError "bad flag \"$arg\", must be one of" $desc
jpayne@68 833 }
jpayne@68 834 proc OptTooManyArgs {desc arguments} {
jpayne@68 835 OptError "too many arguments (unexpected argument(s): $arguments),\
jpayne@68 836 usage:"\
jpayne@68 837 $desc 1
jpayne@68 838 }
jpayne@68 839 proc OptParamType {item} {
jpayne@68 840 if {[OptIsFlag $item]} {
jpayne@68 841 return "flag"
jpayne@68 842 } else {
jpayne@68 843 return "parameter"
jpayne@68 844 }
jpayne@68 845 }
jpayne@68 846 proc OptBadValue {item arg {err {}}} {
jpayne@68 847 # puts "bad val err = \"$err\""
jpayne@68 848 OptError "bad value \"$arg\" for [OptParamType $item]"\
jpayne@68 849 [list $item]
jpayne@68 850 }
jpayne@68 851 proc OptMissingValue {descriptions} {
jpayne@68 852 # set item [OptCurDescFinal $descriptions]
jpayne@68 853 set item [OptCurDesc $descriptions]
jpayne@68 854 OptError "no value given for [OptParamType $item] \"[OptName $item]\"\
jpayne@68 855 (use -help for full usage) :"\
jpayne@68 856 [list $item]
jpayne@68 857 }
jpayne@68 858
jpayne@68 859 proc ::tcl::OptKeyError {prefix descKey {header 0}} {
jpayne@68 860 OptError $prefix [OptKeyGetDesc $descKey] $header
jpayne@68 861 }
jpayne@68 862
jpayne@68 863 # determine string length for nice tabulated output
jpayne@68 864 proc OptLengths {desc nlName tlName dlName} {
jpayne@68 865 upvar $nlName nl
jpayne@68 866 upvar $tlName tl
jpayne@68 867 upvar $dlName dl
jpayne@68 868 foreach item $desc {
jpayne@68 869 if {[OptIsCounter $item]} continue
jpayne@68 870 if {[OptIsPrg $item]} {
jpayne@68 871 OptLengths $item nl tl dl
jpayne@68 872 } else {
jpayne@68 873 SetMax nl [string length [OptName $item]]
jpayne@68 874 SetMax tl [string length [OptType $item]]
jpayne@68 875 set dv [OptTypeArgs $item]
jpayne@68 876 if {[OptState $item] != "header"} {
jpayne@68 877 set dv "($dv)"
jpayne@68 878 }
jpayne@68 879 set l [string length $dv]
jpayne@68 880 # limit the space allocated to potentially big "choices"
jpayne@68 881 if {([OptType $item] != "choice") || ($l<=12)} {
jpayne@68 882 SetMax dl $l
jpayne@68 883 } else {
jpayne@68 884 if {![info exists dl]} {
jpayne@68 885 set dl 0
jpayne@68 886 }
jpayne@68 887 }
jpayne@68 888 }
jpayne@68 889 }
jpayne@68 890 }
jpayne@68 891 # output the tree
jpayne@68 892 proc OptTree {desc nl tl dl} {
jpayne@68 893 set res ""
jpayne@68 894 foreach item $desc {
jpayne@68 895 if {[OptIsCounter $item]} continue
jpayne@68 896 if {[OptIsPrg $item]} {
jpayne@68 897 append res [OptTree $item $nl $tl $dl]
jpayne@68 898 } else {
jpayne@68 899 set dv [OptTypeArgs $item]
jpayne@68 900 if {[OptState $item] != "header"} {
jpayne@68 901 set dv "($dv)"
jpayne@68 902 }
jpayne@68 903 append res [string trimright [format "\n %-*s %-*s %-*s %s" \
jpayne@68 904 $nl [OptName $item] $tl [OptType $item] \
jpayne@68 905 $dl $dv [OptHelp $item]]]
jpayne@68 906 }
jpayne@68 907 }
jpayne@68 908 return $res
jpayne@68 909 }
jpayne@68 910
jpayne@68 911 # Give nice usage string
jpayne@68 912 proc ::tcl::OptError {prefix desc {header 0}} {
jpayne@68 913 # determine length
jpayne@68 914 if {$header} {
jpayne@68 915 # add faked instruction
jpayne@68 916 set h [list [OptNewInst header Var/FlagName Type Value Help]]
jpayne@68 917 lappend h [OptNewInst header ------------ ---- ----- ----]
jpayne@68 918 lappend h [OptNewInst header {(-help} "" "" {gives this help)}]
jpayne@68 919 set desc [concat $h $desc]
jpayne@68 920 }
jpayne@68 921 OptLengths $desc nl tl dl
jpayne@68 922 # actually output
jpayne@68 923 return "$prefix[OptTree $desc $nl $tl $dl]"
jpayne@68 924 }
jpayne@68 925
jpayne@68 926
jpayne@68 927 ################ General Utility functions #######################
jpayne@68 928
jpayne@68 929 #
jpayne@68 930 # List utility functions
jpayne@68 931 # Naming convention:
jpayne@68 932 # "Lvarxxx" take the list VARiable name as argument
jpayne@68 933 # "Lxxxx" take the list value as argument
jpayne@68 934 # (which is not costly with Tcl8 objects system
jpayne@68 935 # as it's still a reference and not a copy of the values)
jpayne@68 936 #
jpayne@68 937
jpayne@68 938 # Is that list empty ?
jpayne@68 939 proc ::tcl::Lempty {list} {
jpayne@68 940 expr {[llength $list]==0}
jpayne@68 941 }
jpayne@68 942
jpayne@68 943 # Gets the value of one leaf of a lists tree
jpayne@68 944 proc ::tcl::Lget {list indexLst} {
jpayne@68 945 if {[llength $indexLst] <= 1} {
jpayne@68 946 return [lindex $list $indexLst]
jpayne@68 947 }
jpayne@68 948 Lget [lindex $list [lindex $indexLst 0]] [lrange $indexLst 1 end]
jpayne@68 949 }
jpayne@68 950 # Sets the value of one leaf of a lists tree
jpayne@68 951 # (we use the version that does not create the elements because
jpayne@68 952 # it would be even slower... needs to be written in C !)
jpayne@68 953 # (nb: there is a non trivial recursive problem with indexes 0,
jpayne@68 954 # which appear because there is no difference between a list
jpayne@68 955 # of 1 element and 1 element alone : [list "a"] == "a" while
jpayne@68 956 # it should be {a} and [listp a] should be 0 while [listp {a b}] would be 1
jpayne@68 957 # and [listp "a b"] maybe 0. listp does not exist either...)
jpayne@68 958 proc ::tcl::Lvarset {listName indexLst newValue} {
jpayne@68 959 upvar $listName list
jpayne@68 960 if {[llength $indexLst] <= 1} {
jpayne@68 961 Lvarset1nc list $indexLst $newValue
jpayne@68 962 } else {
jpayne@68 963 set idx [lindex $indexLst 0]
jpayne@68 964 set targetList [lindex $list $idx]
jpayne@68 965 # reduce refcount on targetList (not really usefull now,
jpayne@68 966 # could be with optimizing compiler)
jpayne@68 967 # Lvarset1 list $idx {}
jpayne@68 968 # recursively replace in targetList
jpayne@68 969 Lvarset targetList [lrange $indexLst 1 end] $newValue
jpayne@68 970 # put updated sub list back in the tree
jpayne@68 971 Lvarset1nc list $idx $targetList
jpayne@68 972 }
jpayne@68 973 }
jpayne@68 974 # Set one cell to a value, eventually create all the needed elements
jpayne@68 975 # (on level-1 of lists)
jpayne@68 976 variable emptyList {}
jpayne@68 977 proc ::tcl::Lvarset1 {listName index newValue} {
jpayne@68 978 upvar $listName list
jpayne@68 979 if {$index < 0} {return -code error "invalid negative index"}
jpayne@68 980 set lg [llength $list]
jpayne@68 981 if {$index >= $lg} {
jpayne@68 982 variable emptyList
jpayne@68 983 for {set i $lg} {$i<$index} {incr i} {
jpayne@68 984 lappend list $emptyList
jpayne@68 985 }
jpayne@68 986 lappend list $newValue
jpayne@68 987 } else {
jpayne@68 988 set list [lreplace $list $index $index $newValue]
jpayne@68 989 }
jpayne@68 990 }
jpayne@68 991 # same as Lvarset1 but no bound checking / creation
jpayne@68 992 proc ::tcl::Lvarset1nc {listName index newValue} {
jpayne@68 993 upvar $listName list
jpayne@68 994 set list [lreplace $list $index $index $newValue]
jpayne@68 995 }
jpayne@68 996 # Increments the value of one leaf of a lists tree
jpayne@68 997 # (which must exists)
jpayne@68 998 proc ::tcl::Lvarincr {listName indexLst {howMuch 1}} {
jpayne@68 999 upvar $listName list
jpayne@68 1000 if {[llength $indexLst] <= 1} {
jpayne@68 1001 Lvarincr1 list $indexLst $howMuch
jpayne@68 1002 } else {
jpayne@68 1003 set idx [lindex $indexLst 0]
jpayne@68 1004 set targetList [lindex $list $idx]
jpayne@68 1005 # reduce refcount on targetList
jpayne@68 1006 Lvarset1nc list $idx {}
jpayne@68 1007 # recursively replace in targetList
jpayne@68 1008 Lvarincr targetList [lrange $indexLst 1 end] $howMuch
jpayne@68 1009 # put updated sub list back in the tree
jpayne@68 1010 Lvarset1nc list $idx $targetList
jpayne@68 1011 }
jpayne@68 1012 }
jpayne@68 1013 # Increments the value of one cell of a list
jpayne@68 1014 proc ::tcl::Lvarincr1 {listName index {howMuch 1}} {
jpayne@68 1015 upvar $listName list
jpayne@68 1016 set newValue [expr {[lindex $list $index]+$howMuch}]
jpayne@68 1017 set list [lreplace $list $index $index $newValue]
jpayne@68 1018 return $newValue
jpayne@68 1019 }
jpayne@68 1020 # Removes the first element of a list
jpayne@68 1021 # and returns the new list value
jpayne@68 1022 proc ::tcl::Lvarpop1 {listName} {
jpayne@68 1023 upvar $listName list
jpayne@68 1024 set list [lrange $list 1 end]
jpayne@68 1025 }
jpayne@68 1026 # Same but returns the removed element
jpayne@68 1027 # (Like the tclX version)
jpayne@68 1028 proc ::tcl::Lvarpop {listName} {
jpayne@68 1029 upvar $listName list
jpayne@68 1030 set el [lindex $list 0]
jpayne@68 1031 set list [lrange $list 1 end]
jpayne@68 1032 return $el
jpayne@68 1033 }
jpayne@68 1034 # Assign list elements to variables and return the length of the list
jpayne@68 1035 proc ::tcl::Lassign {list args} {
jpayne@68 1036 # faster than direct blown foreach (which does not byte compile)
jpayne@68 1037 set i 0
jpayne@68 1038 set lg [llength $list]
jpayne@68 1039 foreach vname $args {
jpayne@68 1040 if {$i>=$lg} break
jpayne@68 1041 uplevel 1 [list ::set $vname [lindex $list $i]]
jpayne@68 1042 incr i
jpayne@68 1043 }
jpayne@68 1044 return $lg
jpayne@68 1045 }
jpayne@68 1046
jpayne@68 1047 # Misc utilities
jpayne@68 1048
jpayne@68 1049 # Set the varname to value if value is greater than varname's current value
jpayne@68 1050 # or if varname is undefined
jpayne@68 1051 proc ::tcl::SetMax {varname value} {
jpayne@68 1052 upvar 1 $varname var
jpayne@68 1053 if {![info exists var] || $value > $var} {
jpayne@68 1054 set var $value
jpayne@68 1055 }
jpayne@68 1056 }
jpayne@68 1057
jpayne@68 1058 # Set the varname to value if value is smaller than varname's current value
jpayne@68 1059 # or if varname is undefined
jpayne@68 1060 proc ::tcl::SetMin {varname value} {
jpayne@68 1061 upvar 1 $varname var
jpayne@68 1062 if {![info exists var] || $value < $var} {
jpayne@68 1063 set var $value
jpayne@68 1064 }
jpayne@68 1065 }
jpayne@68 1066
jpayne@68 1067
jpayne@68 1068 # everything loaded fine, lets create the test proc:
jpayne@68 1069 # OptCreateTestProc
jpayne@68 1070 # Don't need the create temp proc anymore:
jpayne@68 1071 # rename OptCreateTestProc {}
jpayne@68 1072 }