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