3 # (private) Option parsing package
4 # Primarily used internally by the safe:: code.
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.
11 # RCS: @(#) $Id: optparse.tcl,v 1.8.2.1 2003/09/10 20:29:59 dgp Exp $
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
18 namespace eval ::tcl {
21 namespace export OptKeyRegister OptKeyDelete OptKeyError OptKeyParse
\
22 OptProc OptProcArgGiven OptParse
\
24 Lassign Lvarpop Lvarpop1 Lvarset Lvarincr
\
28 ################# Example of use / 'user documentation' ###################
30 proc OptCreateTestProc
{} {
32 # Defines ::tcl::OptParseTest as a test proc with parsed arguments
33 # (can't be defined before the code below is loaded (before "OptProc"))
35 # Every OptProc give usage information on "procname -help".
36 # Try "tcl::OptParseTest -help" and "tcl::OptParseTest -a" and
37 # then other arguments.
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"}
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"}
65 foreach v
[info locals
] {
66 puts stderr
[format "%14s : %s" $v [set $v]]
71 ################### No User serviceable part below ! ###############
73 # Array storing the parsed descriptions
76 # Next potentially free key id (numeric)
79 # Inside algorithm/mechanism description:
80 # (not for the faint hearted ;-)
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.
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
93 # x? means optionally x
95 # "cccc" means the literal string
97 # program :== { programCounter programStep* }
99 # programStep :== program | singleStep
101 # programCounter :== {"P" integer+ }
103 # singleStep :== { instruction parameters* }
105 # instruction :== single element list
107 # (the difference between singleStep and program is that \
108 # llength [lindex $program 0] >= 2
110 # llength [lindex $singleStep 0] == 1
113 # And for this application:
115 # singleStep :== { instruction varname {hasBeenSet currentValue} type
117 # instruction :== "flags" | "value"
118 # type :== knowType | anyword
119 # knowType :== "string" | "int" | "boolean" | "boolflag" | "float"
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
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).
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).
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.
144 # Parse a given description and saves it here under the given key
145 # generate a unused keyid if not given
147 proc ::tcl::OptKeyRegister {desc
{key
""}} {
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
}
157 set program
[list [list "P" 1]];
159 # are we processing flags (which makes a single program step)
164 # flag used to detect that we just have a single (flags set) subprogram.
168 if {$state == "args"} {
169 # more items after 'args'...
170 return -code error "'args' special argument must be the last one";
172 set res
[OptNormalizeOne
$item];
173 set state
[lindex $res 0];
175 if {$state == "flags"} {
176 # add to 'subprogram'
177 lappend flagsprg
$res;
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;
189 if {$state == "flags"} {
191 # sub program counter + first sub program
192 set flagsprg
[list [list "P" 1] $res];
194 lappend program
$res;
201 # We just have the subprogram, optimize and remove
203 set program
$flagsprg;
205 lappend program
$flagsprg;
209 set OptDesc
($key) $program;
215 # Free the storage for that given key
217 proc ::tcl::OptKeyDelete {key
} {
222 # Get the parsed description stored under the given key.
223 proc OptKeyGetDesc
{descKey
} {
225 if {![info exists OptDesc
($descKey)]} {
226 return -code error "Unknown option description key \"$descKey\"";
228 set OptDesc
($descKey);
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;
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
256 # we are relative to some non top level namespace:
257 set key
"${namespace}::${name}";
259 OptKeyRegister
$desc $key;
260 uplevel 1 [list ::proc $name args
"set Args \[::tcl::OptKeyParse $key \$args\]\n$body"];
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
} {
267 expr {[lsearch $alist $argname] >=0}
271 # Programs/Descriptions manipulation
273 # Return the instruction word/list of a given step/(sub)program
274 proc OptInstr
{lst
} {
277 # Is a (sub) program or a plain instruction ?
278 proc OptIsPrg
{lst
} {
279 expr {[llength [OptInstr
$lst]]>=2}
281 # Is this instruction a program counter or a real instr
282 proc OptIsCounter
{item
} {
283 expr {[lindex $item 0]=="P"}
285 # Current program counter (2nd word of first word)
286 proc OptGetPrgCounter
{lst
} {
289 # Current program counter (2nd word of first word)
290 proc OptSetPrgCounter
{lstName newValue
} {
292 set lst
[lreplace $lst 0 0 [concat "P" $newValue]];
294 # returns a list of currently selected items.
295 proc OptSelection
{lst
} {
297 foreach idx
[lrange [lindex $lst 0] 1 end
] {
298 lappend res
[Lget
$lst $idx];
303 # Advance to next description
304 proc OptNextDesc
{descName
} {
305 uplevel 1 [list Lvarincr
$descName {0 1}];
308 # Get the current description, eventually descend
309 proc OptCurDesc
{descriptions
} {
310 lindex $descriptions [OptGetPrgCounter
$descriptions];
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];
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];
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)
340 Lvarset descriptions
$adress [list 1 $value];
344 # empty state means done/paste the end of the program
345 proc OptState
{item
} {
350 proc OptCurState
{descriptions
} {
351 OptState
[OptCurDesc
$descriptions];
355 # Arguments manipulation
357 # Returns the argument that has to be processed now
358 proc OptCurrentArg
{lst
} {
361 # Advance to next argument
362 proc OptNextArg
{argsName
} {
363 uplevel 1 [list Lvarpop1
$argsName];
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
380 set state
[OptCurState
$descriptions];
381 # We'll exit the loop in "OptDoOne" or when state is empty.
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]\
391 OptNextDesc descriptions
;
392 set curitem
[OptCurDesc
$descriptions];
393 set state
[OptCurState
$descriptions];
395 # puts "state = \"$state\" - arguments=($arguments)";
396 if {[Lempty
$state]} {
397 # Nothing left to do, we are done in this branch:
400 # The following statement can make us terminate/continue
401 # as it use return -code {break, continue, return and error}
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];
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
;
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
425 OptCurSetValue descriptions
$arguments;
428 # puts "breaking out ('args' state: consuming every reminding args)"
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)";
437 } elseif
{$state == "optValue"} {
438 set state next
; # not used, for debug only
442 return -code error [OptMissingValue
$descriptions];
445 set arg
[OptCurrentArg
$arguments];
450 # A non-dash argument terminates the options, as does --
453 if {![OptIsFlag
$arg]} {
454 # don't consume the argument, return to previous prg
458 OptNextArg arguments
;
459 if {[string equal
"--" $arg]} {
460 # return from 'flags' state
464 set hits
[OptHits descriptions
$arg];
466 return -code error [OptAmbigous
$descriptions $arg]
467 } elseif
{$hits == 0} {
468 return -code error [OptFlagUsage
$descriptions $arg]
470 set item
[OptCurDesc
$descriptions];
471 if {[OptNeedValue
$item]} {
472 # we need a value, next state is
475 OptCurSetValue descriptions
1;
478 return -code continue;
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]
489 OptNextArg arguments
;
491 OptCurSetValue descriptions
$val;
493 if {$state == "flagValue"} {
495 return -code continue;
497 set state next
; # not used, for debug only
498 return ; # will go on next step
502 set item
[OptCurDesc
$descriptions];
503 # Test the values against their required type
504 if {![catch {OptCheckType
$arg\
505 [OptType
$item] [OptTypeArgs
$item]} val
]} {
508 OptNextArg arguments
;
510 OptCurSetValue descriptions
$val;
513 set state next
; # not used, for debug only
514 return ; # will go on next step
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])";
524 # Parse the options given the key to previously registered description
526 proc ::tcl::OptKeyParse {descKey arglist
} {
528 set desc
[OptKeyGetDesc
$descKey];
530 # make sure -help always give usage
531 if {[string equal
-nocase "-help" $arglist]} {
532 return -code error [OptError
"Usage information:" $desc 1];
535 OptDoAll desc arglist
;
537 if {![Lempty
$arglist]} {
538 return -code error [OptTooManyArgs
$desc $arglist];
542 # Walk through the tree:
543 OptTreeVars
$desc "#[expr {[info level]-1}]" ;
546 # determine string length for nice tabulated output
547 proc OptTreeVars
{desc level
{vnamesLst
{}}} {
549 if {[OptIsCounter
$item]} continue;
550 if {[OptIsPrg
$item]} {
551 set vnamesLst
[OptTreeVars
$item $level $vnamesLst];
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];
564 set var
[OptDefaultValue
$item];
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)";
578 # only types "any", "choice", and numbers can have leading "-"
580 switch -exact -- $type {
582 if {![string is integer
-strict $arg]} {
583 error "not an integer"
588 return [expr {double
($arg)}]
592 # if llength fail : malformed list
593 if {[llength $arg]==0 && [OptIsFlag
$arg]} {
594 error "no values with leading -"
599 if {![string is boolean
-strict $arg]} {
600 error "non canonic boolean"
602 # convert true/false because expr/if is broken with "!,...
603 return [expr {$arg ?
1 : 0}]
606 if {[lsearch -exact $typeArgs $arg] < 0} {
607 error "invalid choice"
616 if {[OptIsFlag
$arg]} {
617 error "no values with leading -"
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
;
635 set larg
[string tolower
$arg];
636 set len
[string length
$larg];
637 set last
[expr {$len-1}];
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]} {
647 OptSetPrgCounter desc
$i;
650 } elseif
{[string equal
$larg [string range
$lflag 0 $last]]} {
657 OptSetPrgCounter desc
$hitems;
662 # Extract fields from the list structure:
664 proc OptName
{item
} {
667 proc OptHasBeenSet
{item
} {
670 proc OptValue
{item
} {
674 proc OptIsFlag
{name
} {
675 string match
"-*" $name;
677 proc OptIsOpt
{name
} {
678 string match
{\?*} $name;
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 "?"];
690 proc OptType
{item
} {
693 proc OptTypeArgs
{item
} {
696 proc OptHelp
{item
} {
699 proc OptNeedValue
{item
} {
700 expr {![string equal
[OptType
$item] boolflag
]}
702 proc OptDefaultValue
{item
} {
703 set val
[OptTypeArgs
$item]
704 switch -exact -- [OptType
$item] {
705 choice
{return [lindex $val 0]}
708 # convert back false/true to 0/1 because expr !$bool
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?\
728 # Generate a canonical form single instruction
729 proc OptNewInst
{state varname type typeArgs help
} {
730 list $state $varname [list 0 {}] $type $typeArgs $help;
733 # hasBeenSet=+ +=currentValue
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];
745 set state
"optValue";
746 } elseif
{![string equal
$varname "args"]} {
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 :-)
759 return [OptNewInst
$state $varname boolflag false
""];
761 return [OptNewInst
$state $varname any
"" ""];
767 set type
[OptGuessType
$arg1]
768 if {[string equal
$type "string"]} {
781 return [OptNewInst
$state $varname $type $def $help];
785 # varname value comment
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 ""];
794 return [OptNewInst
$state $varname $type "" $arg2];
797 return [OptNewInst
$state $varname\
798 [OptGuessType
$arg1] $arg1 $arg2]
802 if {[regexp {^
-(.
+)$} $arg1 x type
]} {
803 return [OptNewInst
$state $varname $type $arg2 $arg3];
805 return -code error [OptOptUsage
$item];
809 return -code error [OptOptUsage
$item];
814 # Auto magic lasy type determination
815 proc OptGuessType
{arg
} {
816 if {[regexp -nocase {^
(true|false
)$} $arg]} {
819 if {[regexp {^
(-+)?
[0-9]+$} $arg]} {
822 if {![catch {expr {double
($arg)}}]} {
828 # Error messages front ends
830 proc OptAmbigous
{desc arg
} {
831 OptError
"ambigous option \"$arg\", choose from:" [OptSelection
$desc]
833 proc OptFlagUsage
{desc arg
} {
834 OptError
"bad flag \"$arg\", must be one of" $desc;
836 proc OptTooManyArgs
{desc arguments
} {
837 OptError
"too many arguments (unexpected argument(s): $arguments),\
841 proc OptParamType
{item
} {
842 if {[OptIsFlag
$item]} {
848 proc OptBadValue
{item arg
{err
{}}} {
849 # puts "bad val err = \"$err\"";
850 OptError
"bad value \"$arg\" for [OptParamType $item]"\
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) :"\
861 proc ::tcl::OptKeyError {prefix descKey
{header
0}} {
862 OptError
$prefix [OptKeyGetDesc
$descKey] $header;
865 # determine string length for nice tabulated output
866 proc OptLengths
{desc nlName tlName dlName
} {
871 if {[OptIsCounter
$item]} continue;
872 if {[OptIsPrg
$item]} {
873 OptLengths
$item nl tl dl
875 SetMax nl
[string length
[OptName
$item]]
876 SetMax tl
[string length
[OptType
$item]]
877 set dv
[OptTypeArgs
$item];
878 if {[OptState
$item] != "header"} {
881 set l
[string length
$dv];
882 # limit the space allocated to potentially big "choices"
883 if {([OptType
$item] != "choice") ||
($l<=12)} {
886 if {![info exists dl
]} {
894 proc OptTree
{desc nl tl dl
} {
897 if {[OptIsCounter
$item]} continue;
898 if {[OptIsPrg
$item]} {
899 append res
[OptTree
$item $nl $tl $dl];
901 set dv
[OptTypeArgs
$item];
902 if {[OptState
$item] != "header"} {
905 append res
[format "\n %-*s %-*s %-*s %s" \
906 $nl [OptName
$item] $tl [OptType
$item] \
907 $dl $dv [OptHelp
$item]]
913 # Give nice usage string
914 proc ::tcl::OptError {prefix desc
{header
0}} {
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]
923 OptLengths
$desc nl tl dl
925 return "$prefix[OptTree $desc $nl $tl $dl]"
929 ################ General Utility functions #######################
932 # List utility functions
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)
940 # Is that list empty ?
941 proc ::tcl::Lempty {list} {
942 expr {[llength $list]==0}
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];
950 Lget
[lindex $list [lindex $indexLst 0]] [lrange $indexLst 1 end
];
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;
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;
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];
985 for {set i
$lg} {$i<$index} {incr i
} {
986 lappend list $emptyList;
988 lappend list $newValue;
990 set list [lreplace $list $index $index $newValue];
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];
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;
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;
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];
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
];
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
];
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)
1040 set lg
[llength $list];
1041 foreach vname
$args {
1043 uplevel 1 [list ::set $vname [lindex $list $i]];
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} {
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} {
1070 # everything loaded fine, lets create the test proc:
1072 # Don't need the create temp proc anymore:
1073 # rename OptCreateTestProc {}