~ubuntu-branches/ubuntu/utopic/electric/utopic-proposed

« back to all changes in this revision

Viewing changes to lib/tcl/opt0.1/optparse.tcl

  • Committer: Bazaar Package Importer
  • Author(s): Onkar Shinde
  • Date: 2008-07-23 02:09:53 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20080723020953-1gmnv7q2wpsdbnop
Tags: 8.07-0ubuntu1
* New Upstream version. Please check changelog for details. (LP: #242720)
* debian/control
  - Add build dependencies *-jdk, cdbs and bsh.
  - Remove build dependency dpatch. We will be using CDBS simple patchsys.
  - Refreshed runtime dependencies to default-jre | java2-runtime and bsh.
  - Added home page field.
  - Standard version 3.8.0.
  - Modify Maintainer value to match the DebianMaintainerField
    specification.
  - Changed email address for original maintainer to indicate who has
    refreshed the packaging.
* debian/rules
  - Revamped to use cdbs.
  - Added get-orig-source target.
* debian/patches
  - 00list, 02_sensible-browser.dpatch, 01_errors-numbers.dpatch,
    03_manpage.dpatch - Deleted, not relevant anymore.
  - 01_fix_build_xml.patch - Patch to fix the build.xml.
* debian/ant.properties
  - File to set various compilation properties.
* debian/electric.1
  - Remove the entry that causes lintian warning.
* debian/electric.desktop
  - Change as suggested by desktop-file-validate.
* debian/electric.docs
  - Updated as per changes in file names.
* debian/electric.svg
  - Name changed from electric_icon.svg.
* debian/install
  - Added appropriate locations for jar file, desktop file and wrapper shell
    script.
* debian/README.source
  - Added to comply with standards version 3.8.0.
* debian/TODO.Debian
  - Name changed form TODO.
* debain/wrapper/electric
  - Wrapper shell script to launch the application.
* debian/manpages
  - Added for installation of manpage.
* debian/watch
  - Updated to match jar files instead of older tar.gz files.
* debian/dirs
  - Removed, not needed anymore.
* debian/{electric.doc-base, electric.examples, substvars}
  - Removed, not relevant anymore.
* debian/*.debhelper
  - Removed auto generated files. Not relevant anymore.

Show diffs side-by-side

added added

removed removed

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