cmdline.tcl

Go to the documentation of this file.
00001 /*  cmdline.tcl --*/
00002 /* */
00003 /*  This package provides a utility for parsing command line*/
00004 /*  arguments that are processed by our various applications.*/
00005 /*  It also includes a utility routine to determine the*/
00006 /*  application name for use in command line errors.*/
00007 /* */
00008 /*  Copyright (c) 1998-2000 by Ajuba Solutions.*/
00009 /*  Copyright (c) 2001-2006 by Andreas Kupries <andreas_kupries@users.sf.net>.*/
00010 /*  Copyright (c) 2003      by David N. Welton  <davidw@dedasys.com>*/
00011 /*  See the file "license.terms" for information on usage and redistribution*/
00012 /*  of this file, and for a DISCLAIMER OF ALL WARRANTIES.*/
00013 /*  */
00014 /*  RCS: @(#) $Id: cmdline.tcl,v 1.24 2006/09/28 02:19:20 andreas_kupries Exp $*/
00015 
00016 package require Tcl 8.2
00017 package provide cmdline 1.3
00018 
00019 namespace ::cmdline {
00020     namespace export getArgv0 getopt getKnownOpt getfiles getoptions \
00021         getKnownOptions usage
00022 }
00023 
00024 /*  ::cmdline::getopt --*/
00025 /* */
00026 /*  The cmdline::getopt works in a fashion like the standard*/
00027 /*  C based getopt function.  Given an option string and a */
00028 /*  pointer to an array or args this command will process the*/
00029 /*  first argument and return info on how to proceed.*/
00030 /* */
00031 /*  Arguments:*/
00032 /*  argvVar     Name of the argv list that you*/
00033 /*          want to process.  If options are found the*/
00034 /*          arg list is modified and the processed arguments*/
00035 /*          are removed from the start of the list.*/
00036 /*  optstring   A list of command options that the application*/
00037 /*          will accept.  If the option ends in ".arg" the*/
00038 /*          getopt routine will use the next argument as */
00039 /*          an argument to the option.  Otherwise the option    */
00040 /*          is a boolean that is set to 1 if present.*/
00041 /*  optVar      The variable pointed to by optVar*/
00042 /*          contains the option that was found (without the*/
00043 /*          leading '-' and without the .arg extension).*/
00044 /*  valVar      Upon success, the variable pointed to by valVar*/
00045 /*          contains the value for the specified option.*/
00046 /*          This value comes from the command line for .arg*/
00047 /*          options, otherwise the value is 1.*/
00048 /*          If getopt fails, the valVar is filled with an*/
00049 /*          error message.*/
00050 /* */
00051 /*  Results:*/
00052 /*      The getopt function returns 1 if an option was found, 0 if no more*/
00053 /*      options were found, and -1 if an error occurred.*/
00054 
00055 ret  ::cmdline::getopt (type argvVar , type optstring , type optVar , type valVar) {
00056     upvar 1 $argvVar argsList
00057     upvar 1 $optVar option
00058     upvar 1 $valVar value
00059 
00060     set result [getKnownOpt argsList $optstring option value]
00061 
00062     if {$result < 0} {
00063         # Collapse unknown-option error into any-other-error result.
00064         set result -1
00065     }
00066     return $result
00067 }
00068 
00069 /*  ::cmdline::getKnownOpt --*/
00070 /* */
00071 /*  The cmdline::getKnownOpt works in a fashion like the standard*/
00072 /*  C based getopt function.  Given an option string and a */
00073 /*  pointer to an array or args this command will process the*/
00074 /*  first argument and return info on how to proceed.*/
00075 /* */
00076 /*  Arguments:*/
00077 /*  argvVar     Name of the argv list that you*/
00078 /*          want to process.  If options are found the*/
00079 /*          arg list is modified and the processed arguments*/
00080 /*          are removed from the start of the list.  Note that*/
00081 /*          unknown options and the args that follow them are*/
00082 /*          left in this list.*/
00083 /*  optstring   A list of command options that the application*/
00084 /*          will accept.  If the option ends in ".arg" the*/
00085 /*          getopt routine will use the next argument as */
00086 /*          an argument to the option.  Otherwise the option    */
00087 /*          is a boolean that is set to 1 if present.*/
00088 /*  optVar      The variable pointed to by optVar*/
00089 /*          contains the option that was found (without the*/
00090 /*          leading '-' and without the .arg extension).*/
00091 /*  valVar      Upon success, the variable pointed to by valVar*/
00092 /*          contains the value for the specified option.*/
00093 /*          This value comes from the command line for .arg*/
00094 /*          options, otherwise the value is 1.*/
00095 /*          If getopt fails, the valVar is filled with an*/
00096 /*          error message.*/
00097 /* */
00098 /*  Results:*/
00099 /*      The getKnownOpt function returns 1 if an option was found,*/
00100 /*  0 if no more options were found, -1 if an unknown option was*/
00101 /*  encountered, and -2 if any other error occurred. */
00102 
00103 ret  ::cmdline::getKnownOpt (type argvVar , type optstring , type optVar , type valVar) {
00104     upvar 1 $argvVar argsList
00105     upvar 1 $optVar  option
00106     upvar 1 $valVar  value
00107 
00108     # default settings for a normal return
00109     set value ""
00110     set option ""
00111     set result 0
00112 
00113     # check if we're past the end of the args list
00114     if {[llength $argsList] != 0} {
00115 
00116     # if we got -- or an option that doesn't begin with -, return (skipping
00117     # the --).  otherwise process the option arg.
00118     switch -glob -- [set arg [lindex $argsList 0]] {
00119         "--" {
00120         set argsList [lrange $argsList 1 end]
00121         }
00122 
00123         "-*" {
00124         set option [string range $arg 1 end]
00125 
00126         if {[lsearch -exact $optstring $option] != -1} {
00127             # Booleans are set to 1 when present
00128             set value 1
00129             set result 1
00130             set argsList [lrange $argsList 1 end]
00131         } elseif {[lsearch -exact $optstring "$option.arg"] != -1} {
00132             set result 1
00133             set argsList [lrange $argsList 1 end]
00134             if {[llength $argsList] != 0} {
00135             set value [lindex $argsList 0]
00136             set argsList [lrange $argsList 1 end]
00137             } else {
00138             set value "Option \"$option\" requires an argument"
00139             set result -2
00140             }
00141         } else {
00142             # Unknown option.
00143             set value "Illegal option \"-$option\""
00144             set result -1
00145         }
00146         }
00147         default {
00148         # Skip ahead
00149         }
00150     }
00151     }
00152 
00153     return $result
00154 }
00155 
00156 /*  ::cmdline::getoptions --*/
00157 /* */
00158 /*  Process a set of command line options, filling in defaults*/
00159 /*  for those not specified.  This also generates an error message*/
00160 /*  that lists the allowed flags if an incorrect flag is specified.*/
00161 /* */
00162 /*  Arguments:*/
00163 /*  arglistVar  The name of the argument list, typically argv.*/
00164 /*          We remove all known options and their args from it.*/
00165 /*  optlist     A list-of-lists where each element specifies an option*/
00166 /*          in the form:*/
00167 /*              (where flag takes no argument) */
00168 /*                  flag comment */
00169 /* */
00170 /*              (or where flag takes an argument) */
00171 /*                  flag default comment*/
00172 /* */
00173 /*          If flag ends in ".arg" then the value is taken from the*/
00174 /*          command line. Otherwise it is a boolean and appears in*/
00175 /*          the result if present on the command line. If flag ends*/
00176 /*          in ".secret", it will not be displayed in the usage.*/
00177 /*  usage       Text to include in the usage display. Defaults to*/
00178 /*          "options:"*/
00179 /* */
00180 /*  Results*/
00181 /*  Name value pairs suitable for using with array set.*/
00182 
00183 ret  ::cmdline::getoptions (type arglistVar , type optlist , optional usage =options:) {
00184     upvar 1 $arglistVar argv
00185 
00186     set opts [GetOptionDefaults $optlist result]
00187 
00188     set argc [llength $argv]
00189     while {[set err [getopt argv $opts opt arg]]} {
00190     if {$err < 0} {
00191             set result(?) ""
00192             break
00193     }
00194     set result($opt) $arg
00195     }
00196     if {[info exist result(?)] || [info exists result(help)]} {
00197     error [usage $optlist $usage]
00198     }
00199     return [array get result]
00200 }
00201 
00202 /*  ::cmdline::getKnownOptions --*/
00203 /* */
00204 /*  Process a set of command line options, filling in defaults*/
00205 /*  for those not specified.  This ignores unknown flags, but generates*/
00206 /*  an error message that lists the correct usage if a known option*/
00207 /*  is used incorrectly.*/
00208 /* */
00209 /*  Arguments:*/
00210 /*  arglistVar  The name of the argument list, typically argv.  This*/
00211 /*          We remove all known options and their args from it.*/
00212 /*  optlist     A list-of-lists where each element specifies an option*/
00213 /*          in the form:*/
00214 /*              flag default comment*/
00215 /*          If flag ends in ".arg" then the value is taken from the*/
00216 /*          command line. Otherwise it is a boolean and appears in*/
00217 /*          the result if present on the command line. If flag ends*/
00218 /*          in ".secret", it will not be displayed in the usage.*/
00219 /*  usage       Text to include in the usage display. Defaults to*/
00220 /*          "options:"*/
00221 /* */
00222 /*  Results*/
00223 /*  Name value pairs suitable for using with array set.*/
00224 
00225 ret  ::cmdline::getKnownOptions (type arglistVar , type optlist , optional usage =options:) {
00226     upvar 1 $arglistVar argv
00227 
00228     set opts [GetOptionDefaults $optlist result]
00229 
00230     # As we encounter them, keep the unknown options and their
00231     # arguments in this list.  Before we return from this procedure,
00232     # we'll prepend these args to the argList so that the application
00233     # doesn't lose them.
00234 
00235     set unknownOptions [list]
00236 
00237     set argc [llength $argv]
00238     while {[set err [getKnownOpt argv $opts opt arg]]} {
00239     if {$err == -1} {
00240             # Unknown option.
00241 
00242             # Skip over any non-option items that follow it.
00243             # For now, add them to the list of unknownOptions.
00244             lappend unknownOptions [lindex $argv 0]
00245             set argv [lrange $argv 1 end]
00246             while {([llength $argv] != 0) \
00247                     && ![string match "-*" [lindex $argv 0]]} {
00248                 lappend unknownOptions [lindex $argv 0]
00249                 set argv [lrange $argv 1 end]
00250             }
00251     } elseif {$err == -2} {
00252             set result(?) ""
00253             break
00254         } else {
00255             set result($opt) $arg
00256         }
00257     }
00258 
00259     # Before returning, prepend the any unknown args back onto the
00260     # argList so that the application doesn't lose them.
00261     set argv [concat $unknownOptions $argv]
00262 
00263     if {[info exist result(?)] || [info exists result(help)]} {
00264     error [usage $optlist $usage]
00265     }
00266     return [array get result]
00267 }
00268 
00269 /*  ::cmdline::GetOptionDefaults --*/
00270 /* */
00271 /*  This internal procedure processes the option list (that was passed to*/
00272 /*  the getopt or getKnownOpt procedure).  The defaultArray gets an index*/
00273 /*  for each option in the option list, the value of which is the option's*/
00274 /*  default value.*/
00275 /* */
00276 /*  Arguments:*/
00277 /*  optlist     A list-of-lists where each element specifies an option*/
00278 /*          in the form:*/
00279 /*              flag default comment*/
00280 /*          If flag ends in ".arg" then the value is taken from the*/
00281 /*          command line. Otherwise it is a boolean and appears in*/
00282 /*          the result if present on the command line. If flag ends*/
00283 /*          in ".secret", it will not be displayed in the usage.*/
00284 /*  defaultArrayVar The name of the array in which to put argument defaults.*/
00285 /* */
00286 /*  Results*/
00287 /*  Name value pairs suitable for using with array set.*/
00288 
00289 ret  ::cmdline::GetOptionDefaults (type optlist , type defaultArrayVar) {
00290     upvar 1 $defaultArrayVar result
00291 
00292     set opts {? help}
00293     foreach opt $optlist {
00294     set name [lindex $opt 0]
00295     if {[regsub -- .secret$ $name {} name] == 1} {
00296         # Need to hide this from the usage display and getopt
00297     }   
00298     lappend opts $name
00299     if {[regsub -- .arg$ $name {} name] == 1} {
00300 
00301         # Set defaults for those that take values.
00302 
00303         set default [lindex $opt 1]
00304         set result($name) $default
00305     } else {
00306         # The default for booleans is false
00307         set result($name) 0
00308     }
00309     }
00310     return $opts
00311 }
00312 
00313 /*  ::cmdline::usage --*/
00314 /* */
00315 /*  Generate an error message that lists the allowed flags.*/
00316 /* */
00317 /*  Arguments:*/
00318 /*  optlist     As for cmdline::getoptions*/
00319 /*  usage       Text to include in the usage display. Defaults to*/
00320 /*          "options:"*/
00321 /* */
00322 /*  Results*/
00323 /*  A formatted usage message*/
00324 
00325 ret  ::cmdline::usage (type optlist , optional usage ={options:)} {
00326     set str "[getArgv0] $usage\n"
00327     foreach opt [concat $optlist \
00328         {{help "Print this message"} {? "Print this message"}}] {
00329      name =  [lindex $opt 0]
00330     if {[regsub -- .secret$ $name {} name] == 1} {
00331         /*  Hidden option*/
00332         continue
00333     }
00334     if {[regsub -- .arg$ $name {} name] == 1} {
00335          default =  [lindex $opt 1]
00336          comment =  [lindex $opt 2]
00337         append str [format " %-20s %s <%s>\n" "-$name value" \
00338             $comment $default]
00339     } else {
00340          comment =  [lindex $opt 1]
00341         append str [format " %-20s %s\n" "-$name" $comment]
00342     }
00343     }
00344     return $str
00345 }
00346 
00347 /*  ::cmdline::getfiles --*/
00348 /* */
00349 /*  Given a list of file arguments from the command line, compute*/
00350 /*  the set of valid files.  On windows, file globbing is performed*/
00351 /*  on each argument.  On Unix, only file existence is tested.  If*/
00352 /*  a file argument produces no valid files, a warning is optionally*/
00353 /*  generated.*/
00354 /* */
00355 /*  This code also uses the full path for each file.  If not*/
00356 /*  given it prepends [pwd] to the filename.  This ensures that*/
00357 /*  these files will never conflict with files in our zip file.*/
00358 /* */
00359 /*  Arguments:*/
00360 /*  patterns    The file patterns specified by the user.*/
00361 /*  quiet       If this flag is set, no warnings will be generated.*/
00362 /* */
00363 /*  Results:*/
00364 /*  Returns the list of files that match the input patterns.*/
00365 
00366 ret  ::cmdline::getfiles (type patterns , type quiet) {
00367     set result {}
00368     if {$::tcl_platform(platform) == "windows"} {
00369     foreach pattern $patterns {
00370         set pat [file join $pattern]
00371         set files [glob -nocomplain -- $pat]
00372         if {$files == {}} {
00373         if {! $quiet} {
00374             puts stdout "warning: no files match \"$pattern\""
00375         }
00376         } else {
00377         foreach file $files {
00378             lappend result $file
00379         }
00380         }
00381     }
00382     } else {
00383     set result $patterns
00384     }
00385     set files {}
00386     foreach file $result {
00387     # Make file an absolute path so that we will never conflict
00388     # with files that might be contained in our zip file.
00389     set fullPath [file join [pwd] $file]
00390     
00391     if {[file isfile $fullPath]} {
00392         lappend files $fullPath
00393     } elseif {! $quiet} {
00394         puts stdout "warning: no files match \"$file\""
00395     }
00396     }
00397     return $files
00398 }
00399 
00400 /*  ::cmdline::getArgv0 --*/
00401 /* */
00402 /*  This command returns the "sanitized" version of argv0.  It will strip*/
00403 /*  off the leading path and remove the ".bin" extensions that our apps*/
00404 /*  use because they must be wrapped by a shell script.*/
00405 /* */
00406 /*  Arguments:*/
00407 /*  None.*/
00408 /* */
00409 /*  Results:*/
00410 /*  The application name that can be used in error messages.*/
00411 
00412 ret  ::cmdline::getArgv0 () {
00413     global argv0
00414 
00415     set name [file tail $argv0]
00416     return [file rootname $name]
00417 }
00418 
00419 /** 
00420  * ### ### ### ######### ######### #########
00421  *#
00422  * Now the typed versions of the above commands.
00423  *#
00424  * ### ### ### ######### ######### #########
00425  *#
00426  */
00427 
00428 /*  typedCmdline.tcl --*/
00429 /* */
00430 /*     This package provides a utility for parsing typed command*/
00431 /*     line arguments that may be processed by various applications.*/
00432 /* */
00433 /*  Copyright (c) 2000 by Ross Palmer Mohn.*/
00434 /*  See the file "license.terms" for information on usage and redistribution*/
00435 /*  of this file, and for a DISCLAIMER OF ALL WARRANTIES.*/
00436 /*  */
00437 /*  RCS: @(#) $Id: cmdline.tcl,v 1.24 2006/09/28 02:19:20 andreas_kupries Exp $*/
00438 
00439 namespace ::cmdline {
00440     namespace export typedGetopt typedGetoptions typedUsage
00441 
00442     /*  variable cmdline::charclasses --*/
00443     /* */
00444     /*     Create regexp list of allowable character classes*/
00445     /*     from "string is" error message.*/
00446     /* */
00447     /*  Results:*/
00448     /*     String of character class names separated by "|" characters.*/
00449 
00450     variable charclasses
00451     catch {string is . .} charclasses
00452     regexp -- {must be (.+)$} $charclasses dummy charclasses
00453     regsub -all -- {, (or )?} $charclasses {|} charclasses
00454 
00455 }
00456 
00457 /*  ::cmdline::typedGetopt --*/
00458 /* */
00459 /*  The cmdline::typedGetopt works in a fashion like the standard*/
00460 /*  C based getopt function.  Given an option string and a*/
00461 /*  pointer to a list of args this command will process the*/
00462 /*  first argument and return info on how to proceed. In addition,*/
00463 /*  you may specify a type for the argument to each option.*/
00464 /* */
00465 /*  Arguments:*/
00466 /*  argvVar     Name of the argv list that you want to process.*/
00467 /*          If options are found, the arg list is modified*/
00468 /*          and the processed arguments are removed from the*/
00469 /*          start of the list.*/
00470 /* */
00471 /*  optstring   A list of command options that the application*/
00472 /*          will accept.  If the option ends in ".xxx", where*/
00473 /*          xxx is any valid character class to the tcl*/
00474 /*          command "string is", then typedGetopt routine will*/
00475 /*          use the next argument as a typed argument to the*/
00476 /*          option. The argument must match the specified*/
00477 /*          character classes (e.g. integer, double, boolean,*/
00478 /*          xdigit, etc.). Alternatively, you may specify*/
00479 /*          ".arg" for an untyped argument.*/
00480 /* */
00481 /*  optVar      Upon success, the variable pointed to by optVar*/
00482 /*          contains the option that was found (without the*/
00483 /*          leading '-' and without the .xxx extension).  If*/
00484 /*          typedGetopt fails the variable is set to the empty*/
00485 /*          string. SOMETIMES! Different for each -value!*/
00486 /* */
00487 /*  argVar      Upon success, the variable pointed to by argVar*/
00488 /*          contains the argument for the specified option.*/
00489 /*          If typedGetopt fails, the variable is filled with*/
00490 /*          an error message.*/
00491 /* */
00492 /*  Argument type syntax:*/
00493 /*  Option that takes no argument.*/
00494 /*      foo*/
00495 /* */
00496 /*  Option that takes a typeless argument.*/
00497 /*      foo.arg*/
00498 /* */
00499 /*  Option that takes a typed argument. Allowable types are all*/
00500 /*  valid character classes to the tcl command "string is".*/
00501 /*  Currently must be one of alnum, alpha, ascii, control,*/
00502 /*  boolean, digit, double, false, graph, integer, lower, print,*/
00503 /*  punct, space, true, upper, wordchar, or xdigit.*/
00504 /*      foo.double*/
00505 /* */
00506 /*  Option that takes an argument from a list.*/
00507 /*      foo.(bar|blat)*/
00508 /* */
00509 /*  Argument quantifier syntax:*/
00510 /*  Option that takes an optional argument.*/
00511 /*      foo.arg?*/
00512 /* */
00513 /*  Option that takes a list of arguments terminated by "--".*/
00514 /*      foo.arg+*/
00515 /* */
00516 /*  Option that takes an optional list of arguments terminated by "--".*/
00517 /*      foo.arg**/
00518 /* */
00519 /*  Argument quantifiers work on all argument types, so, for*/
00520 /*  example, the following is a valid option specification.*/
00521 /*      foo.(bar|blat|blah)?*/
00522 /* */
00523 /*  Argument syntax miscellany:*/
00524 /*  Options may be specified on the command line using a unique,*/
00525 /*  shortened version of the option name. Given that program foo*/
00526 /*  has an option list of {bar.alpha blah.arg blat.double},*/
00527 /*  "foo -b fob" returns an error, but "foo -ba fob"*/
00528 /*  successfully returns {bar fob}*/
00529 /* */
00530 /*  Results:*/
00531 /*  The typedGetopt function returns one of the following:*/
00532 /*   1  a valid option was found*/
00533 /*   0  no more options found to process*/
00534 /*  -1  invalid option*/
00535 /*  -2  missing argument to a valid option*/
00536 /*  -3  argument to a valid option does not match type*/
00537 /* */
00538 /*  Known Bugs:*/
00539 /*  When using options which include special glob characters,*/
00540 /*  you must use the exact option. Abbreviating it can cause*/
00541 /*  an error in the "cmdline::prefixSearch" procedure.*/
00542 
00543 ret  ::cmdline::typedGetopt (type argvVar , type optstring , type optVar , type argVar) {
00544     variable charclasses
00545 
00546     upvar $argvVar argsList
00547 
00548     upvar $optVar retvar
00549     upvar $argVar optarg
00550 
00551     # default settings for a normal return
00552     set optarg ""
00553     set retvar ""
00554     set retval 0
00555 
00556     # check if we're past the end of the args list
00557     if {[llength $argsList] != 0} {
00558 
00559         # if we got -- or an option that doesn't begin with -, return (skipping
00560         # the --).  otherwise process the option arg.
00561         switch -glob -- [set arg [lindex $argsList 0]] {
00562             "--" {
00563                 set argsList [lrange $argsList 1 end]
00564             }
00565 
00566             "-*" {
00567                 # Create list of options without their argument extensions
00568 
00569                 set optstr ""
00570                 foreach str $optstring {
00571                     lappend optstr [file rootname $str]
00572                 }
00573 
00574                 set _opt [string range $arg 1 end]
00575 
00576                 set i [prefixSearch $optstr [file rootname $_opt]]
00577                 if {$i != -1} {
00578                     set opt [lindex $optstring $i]
00579 
00580                     set quantifier "none"
00581                     if {[regexp -- {\.[^.]+([?+*])$} $opt dummy quantifier]} {
00582                         set opt [string range $opt 0 end-1]
00583                     }
00584 
00585                     if {[string first . $opt] == -1} {
00586                         set retval 1
00587                         set retvar $opt
00588                         set argsList [lrange $argsList 1 end]
00589 
00590                     } elseif {[regexp -- "\\.(arg|$charclasses)\$" $opt dummy charclass]
00591                             || [regexp -- {\.\(([^)]+)\)} $opt dummy charclass]} {
00592                 if {[string equal arg $charclass]} {
00593                             set type arg
00594             } elseif {[regexp -- "^($charclasses)\$" $charclass]} {
00595                             set type class
00596                         } else {
00597                             set type oneof
00598                         }
00599 
00600                         set argsList [lrange $argsList 1 end]
00601                         set opt [file rootname $opt]
00602 
00603                         while {1} {
00604                             if {[llength $argsList] == 0
00605                                     || [string equal "--" [lindex $argsList 0]]} {
00606                                 if {[string equal "--" [lindex $argsList 0]]} {
00607                                     set argsList [lrange $argsList 1 end]
00608                                 }
00609 
00610                                 set oneof ""
00611                                 if {$type == "arg"} {
00612                                     set charclass an
00613                                 } elseif {$type == "oneof"} {
00614                                     set oneof ", one of $charclass"
00615                                     set charclass an
00616                                 }
00617     
00618                                 if {$quantifier == "?"} {
00619                                     set retval 1
00620                                     set retvar $opt
00621                                     set optarg ""
00622                                 } elseif {$quantifier == "+"} {
00623                                     set retvar $opt
00624                                     if {[llength $optarg] < 1} {
00625                                         set retval -2
00626                                         set optarg "Option requires at least one $charclass argument$oneof -- $opt"
00627                                     } else {
00628                                         set retval 1
00629                                     }
00630                                 } elseif {$quantifier == "*"} {
00631                                     set retval 1
00632                                     set retvar $opt
00633                                 } else {
00634                                     set optarg "Option requires $charclass argument$oneof -- $opt"
00635                                     set retvar $opt
00636                                     set retval -2
00637                                 }
00638                                 set quantifier ""
00639                             } elseif {($type == "arg")
00640                                     || (($type == "oneof")
00641                                     && [string first "|[lindex $argsList 0]|" "|$charclass|"] != -1)
00642                                     || (($type == "class")
00643                                     && [string is $charclass [lindex $argsList 0]])} {
00644                                 set retval 1
00645                                 set retvar $opt
00646                                 lappend optarg [lindex $argsList 0]
00647                                 set argsList [lrange $argsList 1 end]
00648                             } else {
00649                                 set oneof ""
00650                                 if {$type == "arg"} {
00651                                     set charclass an
00652                                 } elseif {$type == "oneof"} {
00653                                     set oneof ", one of $charclass"
00654                                     set charclass an
00655                                 }
00656                                 set optarg "Option requires $charclass argument$oneof -- $opt"
00657                                 set retvar $opt
00658                                 set retval -3
00659     
00660                                 if {$quantifier == "?"} {
00661                                     set retval 1
00662                                     set optarg ""
00663                                 }
00664                                 set quantifier ""
00665                             }
00666                              if {![regexp -- {[+*]} $quantifier]} {
00667                                 break;
00668                             }
00669                         }
00670                     } else {
00671                         error "Illegal option type specification:\
00672                                 must be one of $charclasses"
00673                     }
00674                 } else {
00675                     set optarg "Illegal option -- $_opt"
00676                     set retvar $_opt
00677                     set retval -1
00678                 }
00679             }
00680         default {
00681         # Skip ahead
00682         }
00683         }
00684     }
00685 
00686     return $retval
00687 }
00688 
00689 /*  ::cmdline::typedGetoptions --*/
00690 /* */
00691 /*  Process a set of command line options, filling in defaults*/
00692 /*  for those not specified. This also generates an error message*/
00693 /*  that lists the allowed options if an incorrect option is*/
00694 /*  specified.*/
00695 /* */
00696 /*  Arguments:*/
00697 /*  arglistVar  The name of the argument list, typically argv*/
00698 /*  optlist     A list-of-lists where each element specifies an option*/
00699 /*          in the form:*/
00700 /* */
00701 /*              option default comment*/
00702 /* */
00703 /*          Options formatting is as described for the optstring*/
00704 /*          argument of typedGetopt. Default is for optionally*/
00705 /*          specifying a default value. Comment is for optionally*/
00706 /*          specifying a comment for the usage display. The*/
00707 /*          options "-help" and "-?" are automatically included*/
00708 /*          in optlist.*/
00709 /* */
00710 /*  Argument syntax miscellany:*/
00711 /*  Options formatting and syntax is as described in typedGetopt.*/
00712 /*  There are two additional suffixes that may be applied when*/
00713 /*  passing options to typedGetoptions.*/
00714 /* */
00715 /*  You may add ".multi" as a suffix to any option. For options*/
00716 /*  that take an argument, this means that the option may be used*/
00717 /*  more than once on the command line and that each additional*/
00718 /*  argument will be appended to a list, which is then returned*/
00719 /*  to the application.*/
00720 /*      foo.double.multi*/
00721 /* */
00722 /*  If a non-argument option is specified as ".multi", it is*/
00723 /*  toggled on and off for each time it is used on the command*/
00724 /*  line.*/
00725 /*      foo.multi*/
00726 /* */
00727 /*  If an option specification does not contain the ".multi"*/
00728 /*  suffix, it is not an error to use an option more than once.*/
00729 /*  In this case, the behavior for options with arguments is that*/
00730 /*  the last argument is the one that will be returned. For*/
00731 /*  options that do not take arguments, using them more than once*/
00732 /*  has no additional effect.*/
00733 /* */
00734 /*  Options may also be hidden from the usage display by*/
00735 /*  appending the suffix ".secret" to any option specification.*/
00736 /*  Please note that the ".secret" suffix must be the last suffix,*/
00737 /*  after any argument type specification and ".multi" suffix.*/
00738 /*      foo.xdigit.multi.secret*/
00739 /* */
00740 /*  Results*/
00741 /*  Name value pairs suitable for using with array set.*/
00742 
00743 ret  ::cmdline::typedGetoptions (type arglistVar , type optlist , optional usage =options:) {
00744     variable charclasses
00745 
00746     upvar 1 $arglistVar argv
00747 
00748     set opts {? help}
00749     foreach opt $optlist {
00750         set name [lindex $opt 0]
00751         if {[regsub -- {\.secret$} $name {} name] == 1} {
00752             # Remove this extension before passing to typedGetopt.
00753         }
00754         if {[regsub -- {\.multi$} $name {} name] == 1} {
00755             # Remove this extension before passing to typedGetopt.
00756 
00757             regsub -- {\..*$} $name {} temp
00758             set multi($temp) 1
00759         }
00760         lappend opts $name
00761         if {[regsub -- "\\.(arg|$charclasses|\\(.+).?\$" $name {} name] == 1} {
00762             # Set defaults for those that take values.
00763             # Booleans are set just by being present, or not
00764 
00765             set dflt [lindex $opt 1]
00766             if {$dflt != {}} {
00767                 set defaults($name) $dflt
00768             }
00769         }
00770     }
00771     set argc [llength $argv]
00772     while {[set err [typedGetopt argv $opts opt arg]]} {
00773         if {$err == 1} {
00774             if {[info exists result($opt)]
00775                     && [info exists multi($opt)]} {
00776                 # Toggle boolean options or append new arguments
00777 
00778                 if {$arg == ""} {
00779                     unset result($opt)
00780                 } else {
00781                     set result($opt) "$result($opt) $arg"
00782                 }
00783             } else {
00784                 set result($opt) "$arg"
00785             }
00786         } elseif {($err == -1) || ($err == -3)} {
00787             error [typedUsage $optlist $usage]
00788         } elseif {$err == -2 && ![info exists defaults($opt)]} {
00789             error [typedUsage $optlist $usage]
00790         }
00791     }
00792     if {[info exists result(?)] || [info exists result(help)]} {
00793         error [typedUsage $optlist $usage]
00794     }
00795     foreach {opt dflt} [array get defaults] {
00796         if {![info exists result($opt)]} {
00797             set result($opt) $dflt
00798         }
00799     }
00800     return [array get result]
00801 }
00802 
00803 /*  ::cmdline::typedUsage --*/
00804 /* */
00805 /*  Generate an error message that lists the allowed flags,*/
00806 /*  type of argument taken (if any), default value (if any),*/
00807 /*  and an optional description.*/
00808 /* */
00809 /*  Arguments:*/
00810 /*  optlist     As for cmdline::typedGetoptions*/
00811 /* */
00812 /*  Results*/
00813 /*  A formatted usage message*/
00814 
00815 ret  ::cmdline::typedUsage (type optlist , optional usage ={options:)} {
00816     variable charclasses
00817 
00818     set str "[getArgv0] $usage\n"
00819     foreach opt [concat $optlist \
00820             {{help "Print this message"} {? "Print this message"}}] {
00821          name =  [lindex $opt 0]
00822         if {[regsub -- {\.secret$} $name {} name] == 1} {
00823             /*  Hidden option*/
00824 
00825         } else {
00826             if {[regsub -- {\.multi$} $name {} name] == 1} {
00827                 /*  Display something about multiple options*/
00828             }
00829 
00830             if {[regexp -- "\\.(arg|$charclasses)\$" $name dummy charclass]
00831                     || [regexp -- {\.\(([^)]+)\)} $opt dummy charclass]} {
00832                    regsub -- "\\..+\$" $name {} name
00833                  comment =  [lindex $opt 2]
00834                  default =  "<[lindex $opt 1]>"
00835                 if {$default == "<>"} {
00836                      default =  ""
00837                 }
00838                 append str [format " %-20s %s %s\n" "-$name $charclass" \
00839                         $comment $default]
00840             } else {
00841                  comment =  [lindex $opt 1]
00842         append str [format " %-20s %s\n" "-$name" $comment]
00843             }
00844         }
00845     }
00846     return $str
00847 }
00848 
00849 /*  ::cmdline::prefixSearch --*/
00850 /* */
00851 /*  Search a Tcl list for a pattern; searches first for an exact match,*/
00852 /*  and if that fails, for a unique prefix that matches the pattern */
00853 /*  (i.e, first "lsearch -exact", then "lsearch -glob $pattern*"*/
00854 /* */
00855 /*  Arguments:*/
00856 /*  list        list of words*/
00857 /*  pattern     word to search for*/
00858 /* */
00859 /*  Results:*/
00860 /*  Index of found word is returned. If no exact match or*/
00861 /*  unique short version is found then -1 is returned.*/
00862 
00863 ret  ::cmdline::prefixSearch (type list , type pattern) {
00864     # Check for an exact match
00865 
00866     if {[set pos [::lsearch -exact $list $pattern]] > -1} {
00867         return $pos
00868     }
00869 
00870     # Check for a unique short version
00871 
00872     set slist [lsort $list]
00873     if {[set pos [::lsearch -glob $slist $pattern*]] > -1} {
00874         # What if there is nothing for the check variable?
00875 
00876         set check [lindex $slist [expr {$pos + 1}]]
00877         if {[string first $pattern $check] != 0} {
00878             return [::lsearch -exact $list [lindex $slist $pos]]
00879         }
00880     }
00881     return -1
00882 }
00883 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1