flags: allow distinct sets of flags.

A FlagSet is an independent set of flags that may be used,
for example, to provide flag processing for subcommands
in a CLI.  The standard, os.Args-derived set of flags is a
global but non-exported FlagSet and the standard functions
are wrappers for methods of that FlagSet.

Allow the programmer to control whether the program
exits if there is a parse error.  For the default set, the behavior
remains to exit on error.

The handling of Usage is odd due to backward compatibility.

R=golang-dev, bradfitz, r, bradfitz
CC=golang-dev
https://golang.org/cl/4517092
This commit is contained in:
Rob Pike 2011-05-22 09:22:00 +10:00
parent 648f25b237
commit f4fe688b09
3 changed files with 340 additions and 149 deletions

View File

@ -9,24 +9,14 @@ import "os"
// Additional routines compiled into the package only during testing. // Additional routines compiled into the package only during testing.
// ResetForTesting clears all flag state and sets the usage function as directed. // ResetForTesting clears all flag state and sets the usage function as directed.
// After calling ResetForTesting, parse errors in flag handling will panic rather // After calling ResetForTesting, parse errors in flag handling will not
// than exit the program. // exit the program.
func ResetForTesting(usage func()) { func ResetForTesting(usage func()) {
flags = &allFlags{make(map[string]*Flag), make(map[string]*Flag), os.Args[1:]} commandLine = NewFlagSet(os.Args[0], ContinueOnError)
Usage = usage Usage = usage
panicOnError = true
} }
// ParseForTesting parses the flag state using the provided arguments. It // CommandLine returns the default FlagSet.
// should be called after 1) ResetForTesting and 2) setting up the new flags. func CommandLine() *FlagSet {
// The return value reports whether the parse was error-free. return commandLine
func ParseForTesting(args []string) (result bool) {
defer func() {
if recover() != nil {
result = false
}
}()
os.Args = args
Parse()
return true
} }

View File

@ -50,18 +50,12 @@
Integer flags accept 1234, 0664, 0x1234 and may be negative. Integer flags accept 1234, 0664, 0x1234 and may be negative.
Boolean flags may be 1, 0, t, f, true, false, TRUE, FALSE, True, False. Boolean flags may be 1, 0, t, f, true, false, TRUE, FALSE, True, False.
It is safe to call flag.Parse multiple times, possibly after changing The default set of command-line flags is controlled by
os.Args. This makes it possible to implement command lines with top-level functions. The FlagSet type allows one to define
subcommands that enable additional flags, as in: independent sets of flags, such as to implement subcommands
in a command-line interface. The methods of FlagSet are
flag.Bool(...) // global options analogous to the top-level functions for the command-line
flag.Parse() // parse leading command flag set.
subcmd := flag.Arg(0)
switch subcmd {
// add per-subcommand options
}
os.Args = flag.Args()
flag.Parse()
*/ */
package flag package flag
@ -190,6 +184,30 @@ type Value interface {
Set(string) bool Set(string) bool
} }
// ErrorHandling defines how to handle flag parsing errors.
type ErrorHandling int
const (
ContinueOnError ErrorHandling = iota
ExitOnError
PanicOnError
)
// A FlagSet represents a set of defined flags.
type FlagSet struct {
// Usage is the function called when an error occurs while parsing flags.
// The field is a function (not a method) that may be changed to point to
// a custom error handler.
Usage func()
name string
actual map[string]*Flag
formal map[string]*Flag
args []string // arguments after flags
exitOnError bool // does the program exit if there's an error?
errorHandling ErrorHandling
}
// A Flag represents the state of a flag. // A Flag represents the state of a flag.
type Flag struct { type Flag struct {
Name string // name as it appears on command line Name string // name as it appears on command line
@ -198,14 +216,6 @@ type Flag struct {
DefValue string // default value (as text); for usage message DefValue string // default value (as text); for usage message
} }
type allFlags struct {
actual map[string]*Flag
formal map[string]*Flag
args []string // arguments after flags
}
var flags *allFlags
// sortFlags returns the flags as a slice in lexicographical sorted order. // sortFlags returns the flags as a slice in lexicographical sorted order.
func sortFlags(flags map[string]*Flag) []*Flag { func sortFlags(flags map[string]*Flag) []*Flag {
list := make(sort.StringArray, len(flags)) list := make(sort.StringArray, len(flags))
@ -224,43 +234,67 @@ func sortFlags(flags map[string]*Flag) []*Flag {
// VisitAll visits the flags in lexicographical order, calling fn for each. // VisitAll visits the flags in lexicographical order, calling fn for each.
// It visits all flags, even those not set. // It visits all flags, even those not set.
func VisitAll(fn func(*Flag)) { func (f *FlagSet) VisitAll(fn func(*Flag)) {
for _, f := range sortFlags(flags.formal) { for _, flag := range sortFlags(f.formal) {
fn(f) fn(flag)
} }
} }
// VisitAll visits the command-line flags in lexicographical order, calling
// fn for each. It visits all flags, even those not set.
func VisitAll(fn func(*Flag)) {
commandLine.VisitAll(fn)
}
// Visit visits the flags in lexicographical order, calling fn for each. // Visit visits the flags in lexicographical order, calling fn for each.
// It visits only those flags that have been set. // It visits only those flags that have been set.
func Visit(fn func(*Flag)) { func (f *FlagSet) Visit(fn func(*Flag)) {
for _, f := range sortFlags(flags.actual) { for _, flag := range sortFlags(f.actual) {
fn(f) fn(flag)
} }
} }
// Visit visits the command-line flags in lexicographical order, calling fn
// for each. It visits only those flags that have been set.
func Visit(fn func(*Flag)) {
commandLine.Visit(fn)
}
// Lookup returns the Flag structure of the named flag, returning nil if none exists. // Lookup returns the Flag structure of the named flag, returning nil if none exists.
func (f *FlagSet) Lookup(name string) *Flag {
return f.formal[name]
}
// Lookup returns the Flag structure of the named command-line flag,
// returning nil if none exists.
func Lookup(name string) *Flag { func Lookup(name string) *Flag {
return flags.formal[name] return commandLine.formal[name]
} }
// Set sets the value of the named flag. It returns true if the set succeeded; false if // Set sets the value of the named flag. It returns true if the set succeeded; false if
// there is no such flag defined. // there is no such flag defined.
func Set(name, value string) bool { func (f *FlagSet) Set(name, value string) bool {
f, ok := flags.formal[name] flag, ok := f.formal[name]
if !ok { if !ok {
return false return false
} }
ok = f.Value.Set(value) ok = flag.Value.Set(value)
if !ok { if !ok {
return false return false
} }
flags.actual[name] = f f.actual[name] = flag
return true return true
} }
// PrintDefaults prints to standard error the default values of all defined flags. // Set sets the value of the named command-line flag. It returns true if the
func PrintDefaults() { // set succeeded; false if there is no such flag defined.
VisitAll(func(f *Flag) { func Set(name, value string) bool {
return commandLine.Set(name, value)
}
// PrintDefaults prints to standard error the default values of all defined flags in the set.
func (f *FlagSet) PrintDefaults() {
f.VisitAll(func(f *Flag) {
format := " -%s=%s: %s\n" format := " -%s=%s: %s\n"
if _, ok := f.Value.(*stringValue); ok { if _, ok := f.Value.(*stringValue); ok {
// put quotes on the value // put quotes on the value
@ -270,141 +304,255 @@ func PrintDefaults() {
}) })
} }
// Usage prints to standard error a default usage message documenting all defined flags. // PrintDefaults prints to standard error the default values of all defined command-line flags.
// The function is a variable that may be changed to point to a custom function. func PrintDefaults() {
var Usage = func() { commandLine.PrintDefaults()
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
PrintDefaults()
} }
var panicOnError = false // defaultUsage is the default function to print a usage message.
func defaultUsage(f *FlagSet) {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", f.name)
f.PrintDefaults()
}
// failf prints to standard error a formatted error and Usage, and then exits the program. // Usage prints to standard error a usage message documenting all defined command-line flags.
func failf(format string, a ...interface{}) { // The function is a variable that may be changed to point to a custom function.
fmt.Fprintf(os.Stderr, format, a...) var Usage = func() {
Usage() defaultUsage(commandLine)
if panicOnError {
panic("flag parse error")
}
os.Exit(2)
} }
// NFlag returns the number of flags that have been set. // NFlag returns the number of flags that have been set.
func NFlag() int { return len(flags.actual) } func (f *FlagSet) NFlag() int { return len(f.actual) }
// NFlag returns the number of command-line flags that have been set.
func NFlag() int { return len(commandLine.actual) }
// Arg returns the i'th argument. Arg(0) is the first remaining argument
// after flags have been processed.
func (f *FlagSet) Arg(i int) string {
if i < 0 || i >= len(f.args) {
return ""
}
return f.args[i]
}
// Arg returns the i'th command-line argument. Arg(0) is the first remaining argument // Arg returns the i'th command-line argument. Arg(0) is the first remaining argument
// after flags have been processed. // after flags have been processed.
func Arg(i int) string { func Arg(i int) string {
if i < 0 || i >= len(flags.args) { return commandLine.Arg(i)
return ""
}
return flags.args[i]
} }
// NArg is the number of arguments remaining after flags have been processed. // NArg is the number of arguments remaining after flags have been processed.
func NArg() int { return len(flags.args) } func (f *FlagSet) NArg() int { return len(f.args) }
// NArg is the number of arguments remaining after flags have been processed.
func NArg() int { return len(commandLine.args) }
// Args returns the non-flag arguments.
func (f *FlagSet) Args() []string { return f.args }
// Args returns the non-flag command-line arguments. // Args returns the non-flag command-line arguments.
func Args() []string { return flags.args } func Args() []string { return commandLine.args }
// BoolVar defines a bool flag with specified name, default value, and usage string.
// The argument p points to a bool variable in which to store the value of the flag.
func (f *FlagSet) BoolVar(p *bool, name string, value bool, usage string) {
f.Var(newBoolValue(value, p), name, usage)
}
// BoolVar defines a bool flag with specified name, default value, and usage string. // BoolVar defines a bool flag with specified name, default value, and usage string.
// The argument p points to a bool variable in which to store the value of the flag. // The argument p points to a bool variable in which to store the value of the flag.
func BoolVar(p *bool, name string, value bool, usage string) { func BoolVar(p *bool, name string, value bool, usage string) {
Var(newBoolValue(value, p), name, usage) commandLine.Var(newBoolValue(value, p), name, usage)
}
// Bool defines a bool flag with specified name, default value, and usage string.
// The return value is the address of a bool variable that stores the value of the flag.
func (f *FlagSet) Bool(name string, value bool, usage string) *bool {
p := new(bool)
f.BoolVar(p, name, value, usage)
return p
} }
// Bool defines a bool flag with specified name, default value, and usage string. // Bool defines a bool flag with specified name, default value, and usage string.
// The return value is the address of a bool variable that stores the value of the flag. // The return value is the address of a bool variable that stores the value of the flag.
func Bool(name string, value bool, usage string) *bool { func Bool(name string, value bool, usage string) *bool {
p := new(bool) return commandLine.Bool(name, value, usage)
BoolVar(p, name, value, usage) }
return p
// IntVar defines an int flag with specified name, default value, and usage string.
// The argument p points to an int variable in which to store the value of the flag.
func (f *FlagSet) IntVar(p *int, name string, value int, usage string) {
f.Var(newIntValue(value, p), name, usage)
} }
// IntVar defines an int flag with specified name, default value, and usage string. // IntVar defines an int flag with specified name, default value, and usage string.
// The argument p points to an int variable in which to store the value of the flag. // The argument p points to an int variable in which to store the value of the flag.
func IntVar(p *int, name string, value int, usage string) { func IntVar(p *int, name string, value int, usage string) {
Var(newIntValue(value, p), name, usage) commandLine.Var(newIntValue(value, p), name, usage)
}
// Int defines an int flag with specified name, default value, and usage string.
// The return value is the address of an int variable that stores the value of the flag.
func (f *FlagSet) Int(name string, value int, usage string) *int {
p := new(int)
f.IntVar(p, name, value, usage)
return p
} }
// Int defines an int flag with specified name, default value, and usage string. // Int defines an int flag with specified name, default value, and usage string.
// The return value is the address of an int variable that stores the value of the flag. // The return value is the address of an int variable that stores the value of the flag.
func Int(name string, value int, usage string) *int { func Int(name string, value int, usage string) *int {
p := new(int) return commandLine.Int(name, value, usage)
IntVar(p, name, value, usage) }
return p
// Int64Var defines an int64 flag with specified name, default value, and usage string.
// The argument p points to an int64 variable in which to store the value of the flag.
func (f *FlagSet) Int64Var(p *int64, name string, value int64, usage string) {
f.Var(newInt64Value(value, p), name, usage)
} }
// Int64Var defines an int64 flag with specified name, default value, and usage string. // Int64Var defines an int64 flag with specified name, default value, and usage string.
// The argument p points to an int64 variable in which to store the value of the flag. // The argument p points to an int64 variable in which to store the value of the flag.
func Int64Var(p *int64, name string, value int64, usage string) { func Int64Var(p *int64, name string, value int64, usage string) {
Var(newInt64Value(value, p), name, usage) commandLine.Var(newInt64Value(value, p), name, usage)
}
// Int64 defines an int64 flag with specified name, default value, and usage string.
// The return value is the address of an int64 variable that stores the value of the flag.
func (f *FlagSet) Int64(name string, value int64, usage string) *int64 {
p := new(int64)
f.Int64Var(p, name, value, usage)
return p
} }
// Int64 defines an int64 flag with specified name, default value, and usage string. // Int64 defines an int64 flag with specified name, default value, and usage string.
// The return value is the address of an int64 variable that stores the value of the flag. // The return value is the address of an int64 variable that stores the value of the flag.
func Int64(name string, value int64, usage string) *int64 { func Int64(name string, value int64, usage string) *int64 {
p := new(int64) return commandLine.Int64(name, value, usage)
Int64Var(p, name, value, usage)
return p
} }
// UintVar defines a uint flag with specified name, default value, and usage string. // UintVar defines a uint flag with specified name, default value, and usage string.
// The argument p points to a uint variable in which to store the value of the flag. // The argument p points to a uint variable in which to store the value of the flag.
func (f *FlagSet) UintVar(p *uint, name string, value uint, usage string) {
f.Var(newUintValue(value, p), name, usage)
}
// UintVar defines a uint flag with specified name, default value, and usage string.
// The argument p points to a uint variable in which to store the value of the flag.
func UintVar(p *uint, name string, value uint, usage string) { func UintVar(p *uint, name string, value uint, usage string) {
Var(newUintValue(value, p), name, usage) commandLine.Var(newUintValue(value, p), name, usage)
} }
// Uint defines a uint flag with specified name, default value, and usage string. // Uint defines a uint flag with specified name, default value, and usage string.
// The return value is the address of a uint variable that stores the value of the flag. // The return value is the address of a uint variable that stores the value of the flag.
func Uint(name string, value uint, usage string) *uint { func (f *FlagSet) Uint(name string, value uint, usage string) *uint {
p := new(uint) p := new(uint)
UintVar(p, name, value, usage) f.UintVar(p, name, value, usage)
return p return p
} }
// Uint defines a uint flag with specified name, default value, and usage string.
// The return value is the address of a uint variable that stores the value of the flag.
func Uint(name string, value uint, usage string) *uint {
return commandLine.Uint(name, value, usage)
}
// Uint64Var defines a uint64 flag with specified name, default value, and usage string.
// The argument p points to a uint64 variable in which to store the value of the flag.
func (f *FlagSet) Uint64Var(p *uint64, name string, value uint64, usage string) {
f.Var(newUint64Value(value, p), name, usage)
}
// Uint64Var defines a uint64 flag with specified name, default value, and usage string. // Uint64Var defines a uint64 flag with specified name, default value, and usage string.
// The argument p points to a uint64 variable in which to store the value of the flag. // The argument p points to a uint64 variable in which to store the value of the flag.
func Uint64Var(p *uint64, name string, value uint64, usage string) { func Uint64Var(p *uint64, name string, value uint64, usage string) {
Var(newUint64Value(value, p), name, usage) commandLine.Var(newUint64Value(value, p), name, usage)
}
// Uint64 defines a uint64 flag with specified name, default value, and usage string.
// The return value is the address of a uint64 variable that stores the value of the flag.
func (f *FlagSet) Uint64(name string, value uint64, usage string) *uint64 {
p := new(uint64)
f.Uint64Var(p, name, value, usage)
return p
} }
// Uint64 defines a uint64 flag with specified name, default value, and usage string. // Uint64 defines a uint64 flag with specified name, default value, and usage string.
// The return value is the address of a uint64 variable that stores the value of the flag. // The return value is the address of a uint64 variable that stores the value of the flag.
func Uint64(name string, value uint64, usage string) *uint64 { func Uint64(name string, value uint64, usage string) *uint64 {
p := new(uint64) return commandLine.Uint64(name, value, usage)
Uint64Var(p, name, value, usage)
return p
} }
// StringVar defines a string flag with specified name, default value, and usage string. // StringVar defines a string flag with specified name, default value, and usage string.
// The argument p points to a string variable in which to store the value of the flag. // The argument p points to a string variable in which to store the value of the flag.
func StringVar(p *string, name, value string, usage string) { func (f *FlagSet) StringVar(p *string, name string, value string, usage string) {
Var(newStringValue(value, p), name, usage) f.Var(newStringValue(value, p), name, usage)
}
// StringVar defines a string flag with specified name, default value, and usage string.
// The argument p points to a string variable in which to store the value of the flag.
func StringVar(p *string, name string, value string, usage string) {
commandLine.Var(newStringValue(value, p), name, usage)
} }
// String defines a string flag with specified name, default value, and usage string. // String defines a string flag with specified name, default value, and usage string.
// The return value is the address of a string variable that stores the value of the flag. // The return value is the address of a string variable that stores the value of the flag.
func String(name, value string, usage string) *string { func (f *FlagSet) String(name string, value string, usage string) *string {
p := new(string) p := new(string)
StringVar(p, name, value, usage) f.StringVar(p, name, value, usage)
return p return p
} }
// String defines a string flag with specified name, default value, and usage string.
// The return value is the address of a string variable that stores the value of the flag.
func String(name string, value string, usage string) *string {
return commandLine.String(name, value, usage)
}
// Float64Var defines a float64 flag with specified name, default value, and usage string.
// The argument p points to a float64 variable in which to store the value of the flag.
func (f *FlagSet) Float64Var(p *float64, name string, value float64, usage string) {
f.Var(newFloat64Value(value, p), name, usage)
}
// Float64Var defines a float64 flag with specified name, default value, and usage string. // Float64Var defines a float64 flag with specified name, default value, and usage string.
// The argument p points to a float64 variable in which to store the value of the flag. // The argument p points to a float64 variable in which to store the value of the flag.
func Float64Var(p *float64, name string, value float64, usage string) { func Float64Var(p *float64, name string, value float64, usage string) {
Var(newFloat64Value(value, p), name, usage) commandLine.Var(newFloat64Value(value, p), name, usage)
} }
// Float64 defines a float64 flag with specified name, default value, and usage string. // Float64 defines a float64 flag with specified name, default value, and usage string.
// The return value is the address of a float64 variable that stores the value of the flag. // The return value is the address of a float64 variable that stores the value of the flag.
func Float64(name string, value float64, usage string) *float64 { func (f *FlagSet) Float64(name string, value float64, usage string) *float64 {
p := new(float64) p := new(float64)
Float64Var(p, name, value, usage) f.Float64Var(p, name, value, usage)
return p return p
} }
// Float64 defines an int flag with specified name, default value, and usage string.
// The return value is the address of a float64 variable that stores the value of the flag.
func Float64(name string, value float64, usage string) *float64 {
return commandLine.Float64(name, value, usage)
}
// Var defines a flag with the specified name and usage string. The type and
// value of the flag are represented by the first argument, of type Value, which
// typically holds a user-defined implementation of Value. For instance, the
// caller could create a flag that turns a comma-separated string into a slice
// of strings by giving the slice the methods of Value; in particular, Set would
// decompose the comma-separated string into the slice.
func (f *FlagSet) Var(value Value, name string, usage string) {
// Remember the default value as a string; it won't change.
flag := &Flag{name, usage, value, value.String()}
_, alreadythere := f.formal[name]
if alreadythere {
fmt.Fprintf(os.Stderr, "%s flag redefined: %s\n", f.name, name)
panic("flag redefinition") // Happens only if flags are declared with identical names
}
f.formal[name] = flag
}
// Var defines a flag with the specified name and usage string. The type and // Var defines a flag with the specified name and usage string. The type and
// value of the flag are represented by the first argument, of type Value, which // value of the flag are represented by the first argument, of type Value, which
// typically holds a user-defined implementation of Value. For instance, the // typically holds a user-defined implementation of Value. For instance, the
@ -412,36 +560,42 @@ func Float64(name string, value float64, usage string) *float64 {
// of strings by giving the slice the methods of Value; in particular, Set would // of strings by giving the slice the methods of Value; in particular, Set would
// decompose the comma-separated string into the slice. // decompose the comma-separated string into the slice.
func Var(value Value, name string, usage string) { func Var(value Value, name string, usage string) {
// Remember the default value as a string; it won't change. commandLine.Var(value, name, usage)
f := &Flag{name, usage, value, value.String()}
_, alreadythere := flags.formal[name]
if alreadythere {
fmt.Fprintln(os.Stderr, "flag redefined:", name)
panic("flag redefinition") // Happens only if flags are declared with identical names
}
flags.formal[name] = f
} }
// failf prints to standard error a formatted error and usage message and
// returns the error.
func (f *FlagSet) failf(format string, a ...interface{}) os.Error {
err := fmt.Errorf(format, a...)
fmt.Println(errc)
if f == commandLine {
Usage()
} else {
f.Usage()
}
return err
}
func (f *allFlags) parseOne() (ok bool) { // parseOne parses one flag. It returns whether a flag was seen.
func (f *FlagSet) parseOne() (bool, os.Error) {
if len(f.args) == 0 { if len(f.args) == 0 {
return false return false, nil
} }
s := f.args[0] s := f.args[0]
if len(s) == 0 || s[0] != '-' || len(s) == 1 { if len(s) == 0 || s[0] != '-' || len(s) == 1 {
return false return false, nil
} }
num_minuses := 1 num_minuses := 1
if s[1] == '-' { if s[1] == '-' {
num_minuses++ num_minuses++
if len(s) == 2 { // "--" terminates the flags if len(s) == 2 { // "--" terminates the flags
f.args = f.args[1:] f.args = f.args[1:]
return false return false, nil
} }
} }
name := s[num_minuses:] name := s[num_minuses:]
if len(name) == 0 || name[0] == '-' || name[0] == '=' { if len(name) == 0 || name[0] == '-' || name[0] == '=' {
failf("bad flag syntax: %s\n", s) return false, f.failf("bad flag syntax: %s", s)
} }
// it's a flag. does it have an argument? // it's a flag. does it have an argument?
@ -456,15 +610,15 @@ func (f *allFlags) parseOne() (ok bool) {
break break
} }
} }
m := flags.formal m := f.formal
flag, alreadythere := m[name] // BUG flag, alreadythere := m[name] // BUG
if !alreadythere { if !alreadythere {
failf("flag provided but not defined: -%s\n", name) return false, f.failf("flag provided but not defined: -%s", name)
} }
if fv, ok := flag.Value.(*boolValue); ok { // special case: doesn't need an arg if fv, ok := flag.Value.(*boolValue); ok { // special case: doesn't need an arg
if has_value { if has_value {
if !fv.Set(value) { if !fv.Set(value) {
failf("invalid boolean value %q for flag: -%s\n", value, name) f.failf("invalid boolean value %q for flag: -%s", value, name)
} }
} else { } else {
fv.Set("true") fv.Set("true")
@ -477,25 +631,61 @@ func (f *allFlags) parseOne() (ok bool) {
value, f.args = f.args[0], f.args[1:] value, f.args = f.args[0], f.args[1:]
} }
if !has_value { if !has_value {
failf("flag needs an argument: -%s\n", name) return false, f.failf("flag needs an argument: -%s", name)
} }
ok = flag.Value.Set(value) ok = flag.Value.Set(value)
if !ok { if !ok {
failf("invalid value %q for flag: -%s\n", value, name) return false, f.failf("invalid value %q for flag: -%s", value, name)
} }
} }
flags.actual[name] = flag f.actual[name] = flag
return true return true, nil
} }
// Parse parses the command-line flags. Must be called after all flags are defined // Parse parses flag definitions from the argument list, which should not
// and before any are accessed by the program. // include the command name. Must be called after all flags in the FlagSet
func Parse() { // are defined and before flags are accessed by the program.
flags.args = os.Args[1:] func (f *FlagSet) Parse(arguments []string) os.Error {
for flags.parseOne() { f.args = arguments
for {
seen, err := f.parseOne()
if seen {
continue
}
if err == nil {
break
}
switch f.errorHandling {
case ContinueOnError:
return err
case ExitOnError:
os.Exit(2)
case PanicOnError:
panic(err)
}
} }
return nil
} }
func init() { // Parse parses the command-line flags from os.Args[1:]. Must be called
flags = &allFlags{make(map[string]*Flag), make(map[string]*Flag), os.Args[1:]} // after all flags are defined and before flags are accessed by the program.
func Parse() {
// Ignore errors; commandLine is set for ExitOnError.
commandLine.Parse(os.Args[1:])
}
// The default set of command-line flags, parsed from os.Args.
var commandLine = NewFlagSet(os.Args[0], ExitOnError)
// NewFlagSet returns a new, empty flag set with the specified name and
// error handling property.
func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet {
f := &FlagSet{
name: name,
actual: make(map[string]*Flag),
formal: make(map[string]*Flag),
errorHandling: errorHandling,
}
f.Usage = func() { defaultUsage(f) }
return f
} }

View File

@ -89,7 +89,7 @@ func TestEverything(t *testing.T) {
func TestUsage(t *testing.T) { func TestUsage(t *testing.T) {
called := false called := false
ResetForTesting(func() { called = true }) ResetForTesting(func() { called = true })
if ParseForTesting([]string{"a.out", "-x"}) { if CommandLine().Parse([]string{"-x"}) == nil {
t.Error("parse did not fail for unknown flag") t.Error("parse did not fail for unknown flag")
} }
if !called { if !called {
@ -97,19 +97,17 @@ func TestUsage(t *testing.T) {
} }
} }
func TestParse(t *testing.T) { func testParse(f *FlagSet, t *testing.T) {
ResetForTesting(func() { t.Error("bad parse") }) boolFlag := f.Bool("bool", false, "bool value")
boolFlag := Bool("bool", false, "bool value") bool2Flag := f.Bool("bool2", false, "bool2 value")
bool2Flag := Bool("bool2", false, "bool2 value") intFlag := f.Int("int", 0, "int value")
intFlag := Int("int", 0, "int value") int64Flag := f.Int64("int64", 0, "int64 value")
int64Flag := Int64("int64", 0, "int64 value") uintFlag := f.Uint("uint", 0, "uint value")
uintFlag := Uint("uint", 0, "uint value") uint64Flag := f.Uint64("uint64", 0, "uint64 value")
uint64Flag := Uint64("uint64", 0, "uint64 value") stringFlag := f.String("string", "0", "string value")
stringFlag := String("string", "0", "string value") float64Flag := f.Float64("float64", 0, "float64 value")
float64Flag := Float64("float64", 0, "float64 value")
extra := "one-extra-argument" extra := "one-extra-argument"
args := []string{ args := []string{
"a.out",
"-bool", "-bool",
"-bool2=true", "-bool2=true",
"--int", "22", "--int", "22",
@ -120,8 +118,8 @@ func TestParse(t *testing.T) {
"-float64", "2718e28", "-float64", "2718e28",
extra, extra,
} }
if !ParseForTesting(args) { if err := f.Parse(args); err != nil {
t.Fatal("parse failed") t.Fatal(err)
} }
if *boolFlag != true { if *boolFlag != true {
t.Error("bool flag should be true, is ", *boolFlag) t.Error("bool flag should be true, is ", *boolFlag)
@ -147,14 +145,23 @@ func TestParse(t *testing.T) {
if *float64Flag != 2718e28 { if *float64Flag != 2718e28 {
t.Error("float64 flag should be 2718e28, is ", *float64Flag) t.Error("float64 flag should be 2718e28, is ", *float64Flag)
} }
if len(Args()) != 1 { if len(f.Args()) != 1 {
t.Error("expected one argument, got", len(Args())) t.Error("expected one argument, got", len(f.Args()))
} else if Args()[0] != extra { } else if f.Args()[0] != extra {
t.Errorf("expected argument %q got %q", extra, Args()[0]) t.Errorf("expected argument %q got %q", extra, f.Args()[0])
} }
} }
// Declare a user-defined flag. func TestParse(t *testing.T) {
ResetForTesting(func() { t.Error("bad parse") })
testParse(CommandLine(), t)
}
func TestFlagSetParse(t *testing.T) {
testParse(NewFlagSet("test", ContinueOnError), t)
}
// Declare a user-defined flag type.
type flagVar []string type flagVar []string
func (f *flagVar) String() string { func (f *flagVar) String() string {
@ -167,11 +174,11 @@ func (f *flagVar) Set(value string) bool {
} }
func TestUserDefined(t *testing.T) { func TestUserDefined(t *testing.T) {
ResetForTesting(func() { t.Fatal("bad parse") }) flags := NewFlagSet("test", ContinueOnError)
var v flagVar var v flagVar
Var(&v, "v", "usage") flags.Var(&v, "v", "usage")
if !ParseForTesting([]string{"a.out", "-v", "1", "-v", "2", "-v=3"}) { if err := flags.Parse([]string{"-v", "1", "-v", "2", "-v=3"}); err != nil {
t.Error("parse failed") t.Error(err)
} }
if len(v) != 3 { if len(v) != 3 {
t.Fatal("expected 3 args; got ", len(v)) t.Fatal("expected 3 args; got ", len(v))
@ -182,13 +189,17 @@ func TestUserDefined(t *testing.T) {
} }
} }
// This tests that one can reset the flags. This still works but not well, and is
// superseded by FlagSet.
func TestChangingArgs(t *testing.T) { func TestChangingArgs(t *testing.T) {
ResetForTesting(func() { t.Fatal("bad parse") }) ResetForTesting(func() { t.Fatal("bad parse") })
oldArgs := os.Args oldArgs := os.Args
defer func() { os.Args = oldArgs }() defer func() { os.Args = oldArgs }()
os.Args = []string{"cmd", "-before", "subcmd", "-after", "args"} os.Args = []string{"cmd", "-before", "subcmd", "-after", "args"}
before := Bool("before", false, "") before := Bool("before", false, "")
Parse() if err := CommandLine().Parse(os.Args[1:]); err != nil {
t.Fatal(err)
}
cmd := Arg(0) cmd := Arg(0)
os.Args = Args() os.Args = Args()
after := Bool("after", false, "") after := Bool("after", false, "")