Start anew
[msysgit.git] / mingw / lib / tcl8.4 / opt0.4 / optparse.tcl
blob43e9b488da523d6c846e876e2788ce95fcbece21
1 # optparse.tcl --
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 {
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
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"}
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]]
71 ################### No User serviceable part below ! ###############
73 # Array storing the parsed descriptions
74 variable OptDesc;
75 array set OptDesc {};
76 # Next potentially free key id (numeric)
77 variable OptDescN 0;
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
92 # x+ means "x x*"
93 # x? means optionally x
94 # x | y means x or y
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
109 # while
110 # llength [lindex $singleStep 0] == 1
113 # And for this application:
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"
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 ""}} {
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;
156 # program counter
157 set program [list [list "P" 1]];
159 # are we processing flags (which makes a single program step)
160 set inflags 0;
162 set state {};
164 # flag used to detect that we just have a single (flags set) subprogram.
165 set empty 1;
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";
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;
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;
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;
209 set OptDesc($key) $program;
211 return $key;
215 # Free the storage for that given key
217 proc ::tcl::OptKeyDelete {key} {
218 variable OptDesc;
219 unset OptDesc($key);
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\"";
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
254 set key $name;
255 } else {
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"];
261 return $key;
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}
270 #######
271 # Programs/Descriptions manipulation
273 # Return the instruction word/list of a given step/(sub)program
274 proc OptInstr {lst} {
275 lindex $lst 0;
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} {
287 Lget $lst {0 1}
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]];
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];
300 return $res;
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];
320 return $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];
329 } else {
330 return $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)
339 lappend adress 2
340 Lvarset descriptions $adress [list 1 $value];
341 # ^hasBeenSet flag
344 # empty state means done/paste the end of the program
345 proc OptState {item} {
346 lindex $item 0
349 # current state
350 proc OptCurState {descriptions} {
351 OptState [OptCurDesc $descriptions];
354 #######
355 # Arguments manipulation
357 # Returns the argument that has to be processed now
358 proc OptCurrentArg {lst} {
359 lindex $lst 0;
361 # Advance to next argument
362 proc OptNextArg {argsName} {
363 uplevel 1 [list Lvarpop1 $argsName];
365 #######
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];
395 # puts "state = \"$state\" - arguments=($arguments)";
396 if {[Lempty $state]} {
397 # Nothing left to do, we are done in this branch:
398 break;
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];
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
424 # in.
425 OptCurSetValue descriptions $arguments;
426 set arguments {};
428 # puts "breaking out ('args' state: consuming every reminding args)"
429 return -code break;
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];
444 } else {
445 set arg [OptCurrentArg $arguments];
448 switch $state {
449 flags {
450 # A non-dash argument terminates the options, as does --
452 # Still a flag ?
453 if {![OptIsFlag $arg]} {
454 # don't consume the argument, return to previous prg
455 return -code return;
457 # consume the flag
458 OptNextArg arguments;
459 if {[string equal "--" $arg]} {
460 # return from 'flags' state
461 return -code return;
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]
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;
477 # continue
478 return -code continue;
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]
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
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;
512 # go to next state
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
525 # and arguments list
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];
541 # Analyse the result
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 {}}} {
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];
568 return $vnamesLst
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 {
581 int {
582 if {![string is integer -strict $arg]} {
583 error "not an integer"
585 return $arg;
587 float {
588 return [expr {double($arg)}]
590 script -
591 list {
592 # if llength fail : malformed list
593 if {[llength $arg]==0 && [OptIsFlag $arg]} {
594 error "no values with leading -"
596 return $arg;
598 boolean {
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}]
605 choice {
606 if {[lsearch -exact $typeArgs $arg] < 0} {
607 error "invalid choice"
609 return $arg;
611 any {
612 return $arg;
614 string -
615 default {
616 if {[OptIsFlag $arg]} {
617 error "no values with leading -"
619 return $arg
622 return neverReached;
625 # internal utilities
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;
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]} {
646 # Exact match case
647 OptSetPrgCounter desc $i;
648 return 1;
650 } elseif {[string equal $larg [string range $lflag 0 $last]]} {
651 lappend hitems $i;
652 incr hits;
654 incr i;
656 if {$hits} {
657 OptSetPrgCounter desc $hitems;
659 return $hits
662 # Extract fields from the list structure:
664 proc OptName {item} {
665 lindex $item 1;
667 proc OptHasBeenSet {item} {
668 Lget $item {2 0};
670 proc OptValue {item} {
671 Lget $item {2 1};
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 "?"];
686 } else {
687 return $name;
690 proc OptType {item} {
691 lindex $item 3
693 proc OptTypeArgs {item} {
694 lindex $item 4
696 proc OptHelp {item} {
697 lindex $item 5
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]}
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
717 return $val
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?}";
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
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";
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 :-)
756 switch $lg {
758 if {$isflag} {
759 return [OptNewInst $state $varname boolflag false ""];
760 } else {
761 return [OptNewInst $state $varname any "" ""];
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 ""
776 set help $arg1
777 } else {
778 set help ""
779 set def $arg1
781 return [OptNewInst $state $varname $type $def $help];
784 # varname type value
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 ""];
793 } else {
794 return [OptNewInst $state $varname $type "" $arg2];
796 } else {
797 return [OptNewInst $state $varname\
798 [OptGuessType $arg1] $arg1 $arg2]
802 if {[regexp {^-(.+)$} $arg1 x type]} {
803 return [OptNewInst $state $varname $type $arg2 $arg3];
804 } else {
805 return -code error [OptOptUsage $item];
808 default {
809 return -code error [OptOptUsage $item];
814 # Auto magic lasy type determination
815 proc OptGuessType {arg} {
816 if {[regexp -nocase {^(true|false)$} $arg]} {
817 return boolean
819 if {[regexp {^(-+)?[0-9]+$} $arg]} {
820 return int
822 if {![catch {expr {double($arg)}}]} {
823 return float
825 return string
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),\
838 usage:"\
839 $desc 1
841 proc OptParamType {item} {
842 if {[OptIsFlag $item]} {
843 return "flag";
844 } else {
845 return "parameter";
848 proc OptBadValue {item arg {err {}}} {
849 # puts "bad val err = \"$err\"";
850 OptError "bad value \"$arg\" for [OptParamType $item]"\
851 [list $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) :"\
858 [list $item]
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} {
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)";
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
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)";
905 append res [format "\n %-*s %-*s %-*s %s" \
906 $nl [OptName $item] $tl [OptType $item] \
907 $dl $dv [OptHelp $item]]
910 return $res;
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]
923 OptLengths $desc nl tl dl
924 # actually output
925 return "$prefix[OptTree $desc $nl $tl $dl]"
929 ################ General Utility functions #######################
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)
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;
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;
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;
988 lappend list $newValue;
989 } else {
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;
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;
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;
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];
1034 return $el;
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;
1046 return $lg;
1049 # Misc utilities
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
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
1070 # everything loaded fine, lets create the test proc:
1071 # OptCreateTestProc
1072 # Don't need the create temp proc anymore:
1073 # rename OptCreateTestProc {}