Hog v9.9.0
cmdline.tcl
Go to the documentation of this file.
1 #https://wiki.tcl-lang.org/page/license
2 #
3 #The following terms apply to the all versions of the core Tcl/Tk
4 #releases, the Tcl/Tk browser plug-in version 2.0, and TclBlend and
5 #Jacl version 1.0. Please note that the TclPro tools are under a
6 #different license agreement. This agreement is part of the standard
7 #Tcl/Tk distribution as the file named "license.terms".
8 #
9 #Tcl/Tk License Terms
10 #
11 #This software is copyrighted by the Regents of the University of
12 #California, Sun Microsystems, Inc., Scriptics Corporation, and other
13 #parties. The following terms apply to all files associated with the
14 #software unless explicitly disclaimed in individual files.
15 #
16 #The authors hereby grant permission to use, copy, modify, distribute,
17 #and license this software and its documentation for any purpose,
18 #provided that existing copyright notices are retained in all copies
19 #and that this notice is included verbatim in any distributions. No
20 #written agreement, license, or royalty fee is required for any of the
21 #authorized uses. Modifications to this software may be copyrighted by
22 #their authors and need not follow the licensing terms described here,
23 #provided that the new terms are clearly indicated on the first page
24 #of each file where they apply.
25 #
26 #IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
27 #FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
28 #ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
29 #DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
30 #POSSIBILITY OF SUCH DAMAGE.
31 #
32 #THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
33 #INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
34 #MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
35 #NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND
36 #THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
37 #MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
38 #
39 #GOVERNMENT USE: If you are acquiring this software on behalf of the
40 #U.S. government, the Government shall have only "Restricted Rights"
41 #in the software and related documentation as defined in the Federal
42 #Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
43 #are acquiring the software on behalf of the Department of Defense,
44 #the software shall be classified as "Commercial Computer Software"
45 #and the Government shall have only "Restricted Rights" as defined in
46 #Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing,
47 #the authors grant the U.S. Government and others acting in its behalf
48 #permission to use and distribute the software in accordance with the
49 #terms specified in this license.
50 
51 # cmdline.tcl --
52 #
53 # This package provides a utility for parsing command line
54 # arguments that are processed by our various applications.
55 # It also includes a utility routine to determine the
56 # application name for use in command line errors.
57 #
58 # Copyright (c) 1998-2000 by Ajuba Solutions.
59 # Copyright (c) 2001-2015 by Andreas Kupries <andreas_kupries@users.sf.net>.
60 # Copyright (c) 2003 by David N. Welton <davidw@dedasys.com>
61 # See the file "license.terms" for information on usage and redistribution
62 # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
63 #
64 # RCS: @(#) $Id: cmdline.tcl,v 1.28 2011/02/23 17:41:52 andreas_kupries Exp $
65 
66 package require Tcl 8.2
67 package provide cmdline 1.3.1
68 
69 namespace eval ::cmdline {
70  namespace export getArgv0 getopt getKnownOpt getfiles getoptions \
71  getKnownOptions usage
72 }
73 
74 # ::cmdline::getopt --
75 #
76 # The cmdline::getopt works in a fashion like the standard
77 # C based getopt function. Given an option string and a
78 # pointer to an array or args this command will process the
79 # first argument and return info on how to proceed.
80 #
81 # Arguments:
82 # argvVar Name of the argv list that you
83 # want to process. If options are found the
84 # arg list is modified and the processed arguments
85 # are removed from the start of the list.
86 # optstring A list of command options that the application
87 # will accept. If the option ends in ".arg" the
88 # getopt routine will use the next argument as
89 # an argument to the option. Otherwise the option
90 # is a boolean that is set to 1 if present.
91 # optVar The variable pointed to by optVar
92 # contains the option that was found (without the
93 # leading '-' and without the .arg extension).
94 # valVar Upon success, the variable pointed to by valVar
95 # contains the value for the specified option.
96 # This value comes from the command line for .arg
97 # options, otherwise the value is 1.
98 # If getopt fails, the valVar is filled with an
99 # error message.
100 #
101 # Results:
102 # The getopt function returns 1 if an option was found, 0 if no more
103 # options were found, and -1 if an error occurred.
104 
105 proc ::cmdline::getopt {argvVar optstring optVar valVar} {
106  upvar 1 $argvVar argsList
107  upvar 1 $optVar option
108  upvar 1 $valVar value
109 
110  ## nagelfar ignore
111  set result [getKnownOpt argsList $optstring option value]
112 
113  if {$result < 0} {
114  # Collapse unknown-option error into any-other-error result.
115  set result -1
116  }
117  return $result
118 }
119 
120 # ::cmdline::getKnownOpt --
121 #
122 # The cmdline::getKnownOpt works in a fashion like the standard
123 # C based getopt function. Given an option string and a
124 # pointer to an array or args this command will process the
125 # first argument and return info on how to proceed.
126 #
127 # Arguments:
128 # argvVar Name of the argv list that you
129 # want to process. If options are found the
130 # arg list is modified and the processed arguments
131 # are removed from the start of the list. Note that
132 # unknown options and the args that follow them are
133 # left in this list.
134 # optstring A list of command options that the application
135 # will accept. If the option ends in ".arg" the
136 # getopt routine will use the next argument as
137 # an argument to the option. Otherwise the option
138 # is a boolean that is set to 1 if present.
139 # optVar The variable pointed to by optVar
140 # contains the option that was found (without the
141 # leading '-' and without the .arg extension).
142 # valVar Upon success, the variable pointed to by valVar
143 # contains the value for the specified option.
144 # This value comes from the command line for .arg
145 # options, otherwise the value is 1.
146 # If getopt fails, the valVar is filled with an
147 # error message.
148 #
149 # Results:
150 # The getKnownOpt function returns 1 if an option was found,
151 # 0 if no more options were found, -1 if an unknown option was
152 # encountered, and -2 if any other error occurred.
153 
154 proc ::cmdline::getKnownOpt {argvVar optstring optVar valVar} {
155  upvar 1 $argvVar argsList
156  upvar 1 $optVar option
157  upvar 1 $valVar value
158 
159  # default settings for a normal return
160  set value ""
161  set option ""
162  set result 0
163 
164  # check if we're past the end of the args list
165  if {[llength $argsList] != 0} {
166 
167  # if we got -- or an option that doesn't begin with -, return (skipping
168  # the --). otherwise process the option arg.
169  switch -glob -- [set arg [lindex $argsList 0]] {
170  "--" {
171  set argsList [lrange $argsList 1 end]
172  }
173  "--*" -
174  "-*" {
175  set option [string range $arg 1 end]
176  if {[string equal [string range $option 0 0] "-"]} {
177  set option [string range $arg 2 end]
178  }
179 
180  # support for format: [-]-option=value
181  set idx [string first "=" $option 1]
182  if {$idx != -1} {
183  set _val [string range $option [expr {$idx+1}] end]
184  set option [string range $option 0 [expr {$idx-1}]]
185  }
186 
187  if {[lsearch -exact $optstring $option] != -1} {
188  # Booleans are set to 1 when present
189  set value 1
190  set result 1
191  set argsList [lrange $argsList 1 end]
192  } elseif {[lsearch -exact $optstring "$option.arg"] != -1} {
193  set result 1
194  set argsList [lrange $argsList 1 end]
195 
196  if {[info exists _val]} {
197  set value $_val
198  } elseif {[llength $argsList]} {
199  set value [lindex $argsList 0]
200  set argsList [lrange $argsList 1 end]
201  } else {
202  set value "Option \"$option\" requires an argument"
203  set result -2
204  }
205  } else {
206  # Unknown option.
207  set value "Illegal option \"-$option\""
208  set result -1
209  }
210  }
211  default {
212  # Skip ahead
213  }
214  }
215  }
216 
217  return $result
218 }
219 
220 # ::cmdline::getoptions --
221 #
222 # Process a set of command line options, filling in defaults
223 # for those not specified. This also generates an error message
224 # that lists the allowed flags if an incorrect flag is specified.
225 #
226 # Arguments:
227 # arglistVar The name of the argument list, typically argv.
228 # We remove all known options and their args from it.
229 # optlist A list-of-lists where each element specifies an option
230 # in the form:
231 # (where flag takes no argument)
232 # flag comment
233 #
234 # (or where flag takes an argument)
235 # flag default comment
236 #
237 # If flag ends in ".arg" then the value is taken from the
238 # command line. Otherwise it is a boolean and appears in
239 # the result if present on the command line. If flag ends
240 # in ".secret", it will not be displayed in the usage.
241 # usage Text to include in the usage display. Defaults to
242 # "options:"
243 #
244 # Results
245 # Name value pairs suitable for using with array set.
246 
247 proc ::cmdline::getoptions {arglistVar optlist {usage options:}} {
248  upvar 1 $arglistVar argv
249 
250  set opts [GetOptionDefaults $optlist result]
251 
252  set argc [llength $argv]
253  while {[set err [getopt argv $opts opt arg]]} {
254  if {$err < 0} {
255  set result(?) ""
256  break
257  }
258  set result($opt) $arg
259  }
260  if {[info exists result(?)] || [info exists result(help)]} {
261  Error [usage $optlist $usage] USAGE
262  }
263  return [array get result]
264 }
265 
266 # ::cmdline::getKnownOptions --
267 #
268 # Process a set of command line options, filling in defaults
269 # for those not specified. This ignores unknown flags, but generates
270 # an error message that lists the correct usage if a known option
271 # is used incorrectly.
272 #
273 # Arguments:
274 # arglistVar The name of the argument list, typically argv. This
275 # We remove all known options and their args from it.
276 # optlist A list-of-lists where each element specifies an option
277 # in the form:
278 # flag default comment
279 # If flag ends in ".arg" then the value is taken from the
280 # command line. Otherwise it is a boolean and appears in
281 # the result if present on the command line. If flag ends
282 # in ".secret", it will not be displayed in the usage.
283 # usage Text to include in the usage display. Defaults to
284 # "options:"
285 #
286 # Results
287 # Name value pairs suitable for using with array set.
288 
289 proc ::cmdline::getKnownOptions {arglistVar optlist {usage options:}} {
290  upvar 1 $arglistVar argv
291 
292  set opts [GetOptionDefaults $optlist result]
293 
294  # As we encounter them, keep the unknown options and their
295  # arguments in this list. Before we return from this procedure,
296  # we'll prepend these args to the argList so that the application
297  # doesn't lose them.
298 
299  set unknownOptions [list]
300 
301  set argc [llength $argv]
302  while {[set err [getKnownOpt argv $opts opt arg]]} {
303  if {$err == -1} {
304  # Unknown option.
305 
306  # Skip over any non-option items that follow it.
307  # For now, add them to the list of unknownOptions.
308  lappend unknownOptions [lindex $argv 0]
309  set argv [lrange $argv 1 end]
310  while {([llength $argv] != 0) \
311  && ![string match "-*" [lindex $argv 0]]} {
312  lappend unknownOptions [lindex $argv 0]
313  set argv [lrange $argv 1 end]
314  }
315  } elseif {$err == -2} {
316  set result(?) ""
317  break
318  } else {
319  set result($opt) $arg
320  }
321  }
322 
323  # Before returning, prepend the any unknown args back onto the
324  # argList so that the application doesn't lose them.
325  set argv [concat $unknownOptions $argv]
326 
327  if {[info exists result(?)] || [info exists result(help)]} {
328  Error [usage $optlist $usage] USAGE
329  }
330  return [array get result]
331 }
332 
333 # ::cmdline::GetOptionDefaults --
334 #
335 # This internal procedure processes the option list (that was passed to
336 # the getopt or getKnownOpt procedure). The defaultArray gets an index
337 # for each option in the option list, the value of which is the option's
338 # default value.
339 #
340 # Arguments:
341 # optlist A list-of-lists where each element specifies an option
342 # in the form:
343 # flag default comment
344 # If flag ends in ".arg" then the value is taken from the
345 # command line. Otherwise it is a boolean and appears in
346 # the result if present on the command line. If flag ends
347 # in ".secret", it will not be displayed in the usage.
348 # defaultArrayVar The name of the array in which to put argument defaults.
349 #
350 # Results
351 # Name value pairs suitable for using with array set.
352 
353 proc ::cmdline::GetOptionDefaults {optlist defaultArrayVar} {
354  upvar 1 $defaultArrayVar result
355 
356  set opts {? help}
357  foreach opt $optlist {
358  set name [lindex $opt 0]
359  if {[regsub -- {\.secret$} $name {} name] == 1} {
360  # Need to hide this from the usage display and getopt
361  }
362  lappend opts $name
363  if {[regsub -- {\.arg$} $name {} name] == 1} {
364 
365  # Set defaults for those that take values.
366 
367  set default [lindex $opt 1]
368  set result($name) $default
369  } else {
370  # The default for booleans is false
371  set result($name) 0
372  }
373  }
374  return $opts
375 }
376 
377 # ::cmdline::usage --
378 #
379 # Generate an error message that lists the allowed flags.
380 #
381 # Arguments:
382 # optlist As for cmdline::getoptions
383 # usage Text to include in the usage display. Defaults to
384 # "options:"
385 #
386 # Results
387 # A formatted usage message
388 
389 proc ::cmdline::usage {optlist {usage {options:}}} {
390  set str "[getArgv0] $usage\n"
391  foreach opt [concat $optlist \
392  {{- "Forcibly stop option processing"} {help "Print this message"} {? "Print this message"}}] {
393  set name [lindex $opt 0]
394  if {[regsub -- {\.secret$} $name {} name] == 1} {
395  # Hidden option
396  continue
397  }
398  if {[regsub -- {\.arg$} $name {} name] == 1} {
399  set default [lindex $opt 1]
400  set comment [lindex $opt 2]
401  append str [format " %-20s %s <%s>\n" "-$name value" \
402  $comment $default]
403  } else {
404  set comment [lindex $opt 1]
405  append str [format " %-20s %s\n" "-$name" $comment]
406  }
407  }
408  return $str
409 }
410 
411 # ::cmdline::getfiles --
412 #
413 # Given a list of file arguments from the command line, compute
414 # the set of valid files. On windows, file globbing is performed
415 # on each argument. On Unix, only file existence is tested. If
416 # a file argument produces no valid files, a warning is optionally
417 # generated.
418 #
419 # This code also uses the full path for each file. If not
420 # given it prepends [pwd] to the filename. This ensures that
421 # these files will never conflict with files in our zip file.
422 #
423 # Arguments:
424 # patterns The file patterns specified by the user.
425 # quiet If this flag is set, no warnings will be generated.
426 #
427 # Results:
428 # Returns the list of files that match the input patterns.
429 
430 proc ::cmdline::getfiles {patterns quiet} {
431  set result {}
432  if {$::tcl_platform(platform) == "windows"} {
433  foreach pattern $patterns {
434  set pat [file join $pattern]
435  set files [glob -nocomplain -- $pat]
436  if {$files == {}} {
437  if {! $quiet} {
438  puts stdout "warning: no files match \"$pattern\""
439  }
440  } else {
441  foreach file $files {
442  lappend result $file
443  }
444  }
445  }
446  } else {
447  set result $patterns
448  }
449  set files {}
450  foreach file $result {
451  # Make file an absolute path so that we will never conflict
452  # with files that might be contained in our zip file.
453  set fullPath [file join [pwd] $file]
454 
455  if {[file isfile $fullPath]} {
456  lappend files $fullPath
457  } elseif {! $quiet} {
458  puts stdout "warning: no files match \"$file\""
459  }
460  }
461  return $files
462 }
463 
464 # ::cmdline::getArgv0 --
465 #
466 # This command returns the "sanitized" version of argv0. It will strip
467 # off the leading path and remove the ".bin" extensions that our apps
468 # use because they must be wrapped by a shell script.
469 #
470 # Arguments:
471 # None.
472 #
473 # Results:
474 # The application name that can be used in error messages.
475 
476 proc ::cmdline::getArgv0 {} {
477  global argv0
478 
479  set name [file tail $argv0]
480  return [file rootname $name]
481 }
482 
483 ##
484 # ### ### ### ######### ######### #########
485 ##
486 # Now the typed versions of the above commands.
487 ##
488 # ### ### ### ######### ######### #########
489 ##
490 
491 # typedCmdline.tcl --
492 #
493 # This package provides a utility for parsing typed command
494 # line arguments that may be processed by various applications.
495 #
496 # Copyright (c) 2000 by Ross Palmer Mohn.
497 # See the file "license.terms" for information on usage and redistribution
498 # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
499 #
500 # RCS: @(#) $Id: cmdline.tcl,v 1.28 2011/02/23 17:41:52 andreas_kupries Exp $
501 
502 namespace eval ::cmdline {
503  namespace export typedGetopt typedGetoptions typedUsage
504 
505  # variable cmdline::charclasses --
506  #
507  # Create regexp list of allowable character classes
508  # from "string is" error message.
509  #
510  # Results:
511  # String of character class names separated by "|" characters.
512 
513  variable charclasses
514  #checker exclude badKey
515  ##nagelfar ignore
516  catch {string is . .} charclasses
517  variable dummy
518  regexp -- {must be (.+)$} $charclasses dummy charclasses
519  regsub -all -- {, (or )?} $charclasses {|} charclasses
520  unset dummy
521 }
522 
523 # ::cmdline::typedGetopt --
524 #
525 # The cmdline::typedGetopt works in a fashion like the standard
526 # C based getopt function. Given an option string and a
527 # pointer to a list of args this command will process the
528 # first argument and return info on how to proceed. In addition,
529 # you may specify a type for the argument to each option.
530 #
531 # Arguments:
532 # argvVar Name of the argv list that you want to process.
533 # If options are found, the arg list is modified
534 # and the processed arguments are removed from the
535 # start of the list.
536 #
537 # optstring A list of command options that the application
538 # will accept. If the option ends in ".xxx", where
539 # xxx is any valid character class to the tcl
540 # command "string is", then typedGetopt routine will
541 # use the next argument as a typed argument to the
542 # option. The argument must match the specified
543 # character classes (e.g. integer, double, boolean,
544 # xdigit, etc.). Alternatively, you may specify
545 # ".arg" for an untyped argument.
546 #
547 # optVar Upon success, the variable pointed to by optVar
548 # contains the option that was found (without the
549 # leading '-' and without the .xxx extension). If
550 # typedGetopt fails the variable is set to the empty
551 # string. SOMETIMES! Different for each -value!
552 #
553 # argVar Upon success, the variable pointed to by argVar
554 # contains the argument for the specified option.
555 # If typedGetopt fails, the variable is filled with
556 # an error message.
557 #
558 # Argument type syntax:
559 # Option that takes no argument.
560 # foo
561 #
562 # Option that takes a typeless argument.
563 # foo.arg
564 #
565 # Option that takes a typed argument. Allowable types are all
566 # valid character classes to the tcl command "string is".
567 # Currently must be one of alnum, alpha, ascii, control,
568 # boolean, digit, double, false, graph, integer, lower, print,
569 # punct, space, true, upper, wordchar, or xdigit.
570 # foo.double
571 #
572 # Option that takes an argument from a list.
573 # foo.(bar|blat)
574 #
575 # Argument quantifier syntax:
576 # Option that takes an optional argument.
577 # foo.arg?
578 #
579 # Option that takes a list of arguments terminated by "--".
580 # foo.arg+
581 #
582 # Option that takes an optional list of arguments terminated by "--".
583 # foo.arg*
584 #
585 # Argument quantifiers work on all argument types, so, for
586 # example, the following is a valid option specification.
587 # foo.(bar|blat|blah)?
588 #
589 # Argument syntax miscellany:
590 # Options may be specified on the command line using a unique,
591 # shortened version of the option name. Given that program foo
592 # has an option list of {bar.alpha blah.arg blat.double},
593 # "foo -b fob" returns an error, but "foo -ba fob"
594 # successfully returns {bar fob}
595 #
596 # Results:
597 # The typedGetopt function returns one of the following:
598 # 1 a valid option was found
599 # 0 no more options found to process
600 # -1 invalid option
601 # -2 missing argument to a valid option
602 # -3 argument to a valid option does not match type
603 #
604 # Known Bugs:
605 # When using options which include special glob characters,
606 # you must use the exact option. Abbreviating it can cause
607 # an error in the "cmdline::prefixSearch" procedure.
608 
609 proc ::cmdline::typedGetopt {argvVar optstring optVar argVar} {
610  variable charclasses
611 
612  upvar $argvVar argsList
613 
614  upvar $optVar retvar
615  upvar $argVar optarg
616 
617  # default settings for a normal return
618  set optarg ""
619  set retvar ""
620  set retval 0
621 
622  # check if we're past the end of the args list
623  if {[llength $argsList] != 0} {
624 
625  # if we got -- or an option that doesn't begin with -, return (skipping
626  # the --). otherwise process the option arg.
627  switch -glob -- [set arg [lindex $argsList 0]] {
628  "--" {
629  set argsList [lrange $argsList 1 end]
630  }
631 
632  "-*" {
633  # Create list of options without their argument extensions
634 
635  set optstr ""
636  foreach str $optstring {
637  lappend optstr [file rootname $str]
638  }
639 
640  set _opt [string range $arg 1 end]
641 
642  set i [prefixSearch $optstr [file rootname $_opt]]
643  if {$i != -1} {
644  set opt [lindex $optstring $i]
645 
646  set quantifier "none"
647  if {[regexp -- {\.[^.]+([?+*])$} $opt dummy quantifier]} {
648  set opt [string range $opt 0 end-1]
649  }
650 
651  if {[string first . $opt] == -1} {
652  set retval 1
653  set retvar $opt
654  set argsList [lrange $argsList 1 end]
655 
656  } elseif {[regexp -- "\\.(arg|$charclasses)\$" $opt dummy charclass]
657  || [regexp -- {\.\(([^)]+)\)} $opt dummy charclass]} {
658  ##nagelfar ignore
659  if {[string equal arg $charclass]} {
660  ##nagelfar ignore
661  set type arg
662  } elseif {[regexp -- "^($charclasses)\$" $charclass]} {
663  set type class
664  } else {
665  set type oneof
666  }
667 
668  set argsList [lrange $argsList 1 end]
669  set opt [file rootname $opt]
670 
671  while {1} {
672  if {[llength $argsList] == 0
673  || [string equal "--" [lindex $argsList 0]]} {
674  if {[string equal "--" [lindex $argsList 0]]} {
675  set argsList [lrange $argsList 1 end]
676  }
677 
678  set oneof ""
679  if {$type == "arg"} {
680  set charclass an
681  } elseif {$type == "oneof"} {
682  set oneof ", one of $charclass"
683  set charclass an
684  }
685 
686  if {$quantifier == "?"} {
687  set retval 1
688  set retvar $opt
689  set optarg ""
690  } elseif {$quantifier == "+"} {
691  set retvar $opt
692  if {[llength $optarg] < 1} {
693  set retval -2
694  set optarg "Option requires at least one $charclass argument$oneof -- $opt"
695  } else {
696  set retval 1
697  }
698  } elseif {$quantifier == "*"} {
699  set retval 1
700  set retvar $opt
701  } else {
702  set optarg "Option requires $charclass argument$oneof -- $opt"
703  set retvar $opt
704  set retval -2
705  }
706  set quantifier ""
707  ##nagelfar ignore
708  } elseif {($type == "arg") || (($type == "oneof") && [string first "|[lindex $argsList 0]|" "|$charclass|"] != -1) || (($type == "class") && [string is $charclass [lindex $argsList 0]])} {
709  set retval 1
710  set retvar $opt
711  lappend optarg [lindex $argsList 0]
712  set argsList [lrange $argsList 1 end]
713  } else {
714  set oneof ""
715  if {$type == "arg"} {
716  set charclass an
717  } elseif {$type == "oneof"} {
718  set oneof ", one of $charclass"
719  set charclass an
720  }
721  set optarg "Option requires $charclass argument$oneof -- $opt"
722  set retvar $opt
723  set retval -3
724 
725  if {$quantifier == "?"} {
726  set retval 1
727  set optarg ""
728  }
729  set quantifier ""
730  }
731  if {![regexp -- {[+*]} $quantifier]} {
732  break;
733  }
734  }
735  } else {
736  Error \
737  "Illegal option type specification: must be one of $charclasses" \
738  BAD OPTION TYPE
739  }
740  } else {
741  set optarg "Illegal option -- $_opt"
742  set retvar $_opt
743  set retval -1
744  }
745  }
746  default {
747  # Skip ahead
748  }
749  }
750  }
751 
752  return $retval
753 }
754 
755 # ::cmdline::typedGetoptions --
756 #
757 # Process a set of command line options, filling in defaults
758 # for those not specified. This also generates an error message
759 # that lists the allowed options if an incorrect option is
760 # specified.
761 #
762 # Arguments:
763 # arglistVar The name of the argument list, typically argv
764 # optlist A list-of-lists where each element specifies an option
765 # in the form:
766 #
767 # option default comment
768 #
769 # Options formatting is as described for the optstring
770 # argument of typedGetopt. Default is for optionally
771 # specifying a default value. Comment is for optionally
772 # specifying a comment for the usage display. The
773 # options "--", "-help", and "-?" are automatically included
774 # in optlist.
775 #
776 # Argument syntax miscellany:
777 # Options formatting and syntax is as described in typedGetopt.
778 # There are two additional suffixes that may be applied when
779 # passing options to typedGetoptions.
780 #
781 # You may add ".multi" as a suffix to any option. For options
782 # that take an argument, this means that the option may be used
783 # more than once on the command line and that each additional
784 # argument will be appended to a list, which is then returned
785 # to the application.
786 # foo.double.multi
787 #
788 # If a non-argument option is specified as ".multi", it is
789 # toggled on and off for each time it is used on the command
790 # line.
791 # foo.multi
792 #
793 # If an option specification does not contain the ".multi"
794 # suffix, it is not an error to use an option more than once.
795 # In this case, the behavior for options with arguments is that
796 # the last argument is the one that will be returned. For
797 # options that do not take arguments, using them more than once
798 # has no additional effect.
799 #
800 # Options may also be hidden from the usage display by
801 # appending the suffix ".secret" to any option specification.
802 # Please note that the ".secret" suffix must be the last suffix,
803 # after any argument type specification and ".multi" suffix.
804 # foo.xdigit.multi.secret
805 #
806 # Results
807 # Name value pairs suitable for using with array set.
808 
809 proc ::cmdline::typedGetoptions {arglistVar optlist {usage options:}} {
810  variable charclasses
811 
812  upvar 1 $arglistVar argv
813 
814  set opts {? help}
815  foreach opt $optlist {
816  set name [lindex $opt 0]
817  if {[regsub -- {\.secret$} $name {} name] == 1} {
818  # Remove this extension before passing to typedGetopt.
819  }
820  if {[regsub -- {\.multi$} $name {} name] == 1} {
821  # Remove this extension before passing to typedGetopt.
822 
823  regsub -- {\..*$} $name {} temp
824  set multi($temp) 1
825  }
826  lappend opts $name
827  if {[regsub -- "\\.(arg|$charclasses|\\(.+).?\$" $name {} name] == 1} {
828  # Set defaults for those that take values.
829  # Booleans are set just by being present, or not
830 
831  set dflt [lindex $opt 1]
832  if {$dflt != {}} {
833  set defaults($name) $dflt
834  }
835  }
836  }
837  set argc [llength $argv]
838  while {[set err [typedGetopt argv $opts opt arg]]} {
839  if {$err == 1} {
840  if {[info exists result($opt)]
841  && [info exists multi($opt)]} {
842  # Toggle boolean options or append new arguments
843 
844  if {$arg == ""} {
845  unset result($opt)
846  } else {
847  set result($opt) "$result($opt) $arg"
848  }
849  } else {
850  set result($opt) "$arg"
851  }
852  } elseif {($err == -1) || ($err == -3)} {
853  Error [typedUsage $optlist $usage] USAGE
854  } elseif {$err == -2 && ![info exists defaults($opt)]} {
855  Error [typedUsage $optlist $usage] USAGE
856  }
857  }
858  if {[info exists result(?)] || [info exists result(help)]} {
859  Error [typedUsage $optlist $usage] USAGE
860  }
861  foreach {opt dflt} [array get defaults] {
862  if {![info exists result($opt)]} {
863  set result($opt) $dflt
864  }
865  }
866  return [array get result]
867 }
868 
869 # ::cmdline::typedUsage --
870 #
871 # Generate an error message that lists the allowed flags,
872 # type of argument taken (if any), default value (if any),
873 # and an optional description.
874 #
875 # Arguments:
876 # optlist As for cmdline::typedGetoptions
877 #
878 # Results
879 # A formatted usage message
880 
881 proc ::cmdline::typedUsage {optlist {usage {options:}}} {
882  variable charclasses
883 
884  set str "[getArgv0] $usage\n"
885  foreach opt [concat $optlist \
886  {{help "Print this message"} {? "Print this message"}}] {
887  set name [lindex $opt 0]
888  if {[regsub -- {\.secret$} $name {} name] == 1} {
889  # Hidden option
890 
891  } else {
892  if {[regsub -- {\.multi$} $name {} name] == 1} {
893  # Display something about multiple options
894  }
895 
896  if {[regexp -- "\\.(arg|$charclasses)\$" $name dummy charclass]
897  || [regexp -- {\.\(([^)]+)\)} $opt dummy charclass]} {
898  regsub -- "\\..+\$" $name {} name
899  set comment [lindex $opt 2]
900  set default "<[lindex $opt 1]>"
901  if {$default == "<>"} {
902  set default ""
903  }
904  append str [format " %-20s %s %s\n" "-$name $charclass" \
905  $comment $default]
906  } else {
907  set comment [lindex $opt 1]
908  append str [format " %-20s %s\n" "-$name" $comment]
909  }
910  }
911  }
912  return $str
913 }
914 
915 # ::cmdline::prefixSearch --
916 #
917 # Search a Tcl list for a pattern; searches first for an exact match,
918 # and if that fails, for a unique prefix that matches the pattern
919 # (i.e, first "lsearch -exact", then "lsearch -glob $pattern*"
920 #
921 # Arguments:
922 # list list of words
923 # pattern word to search for
924 #
925 # Results:
926 # Index of found word is returned. If no exact match or
927 # unique short version is found then -1 is returned.
928 
929 proc ::cmdline::prefixSearch {list pattern} {
930  # Check for an exact match
931 
932  if {[set pos [::lsearch -exact $list $pattern]] > -1} {
933  return $pos
934  }
935 
936  # Check for a unique short version
937 
938  set slist [lsort $list]
939  if {[set pos [::lsearch -glob $slist $pattern*]] > -1} {
940  # What if there is nothing for the check variable?
941 
942  set check [lindex $slist [expr {$pos + 1}]]
943  if {[string first $pattern $check] != 0} {
944  return [::lsearch -exact $list [lindex $slist $pos]]
945  }
946  }
947  return -1
948 }
949 # ::cmdline::Error --
950 #
951 # Internal helper to throw errors with a proper error-code attached.
952 #
953 # Arguments:
954 # message text of the error message to throw.
955 # args additional parts of the error code to use,
956 # with CMDLINE as basic prefix added by this command.
957 #
958 # Results:
959 # An error is thrown, always.
960 
961 proc ::cmdline::Error {message args} {
962  return -code error -errorcode [linsert $args 0 CMDLINE] $message
963 }