summaryrefslogtreecommitdiff
path: root/src/pkg/flag
diff options
context:
space:
mode:
authorOndřej Surý <ondrej@sury.org>2012-04-06 15:14:11 +0200
committerOndřej Surý <ondrej@sury.org>2012-04-06 15:14:11 +0200
commit505c19580e0f43fe5224431459cacb7c21edd93d (patch)
tree79e2634c253d60afc0cc0b2f510dc7dcbb48497b /src/pkg/flag
parent1336a7c91e596c423a49d1194ea42d98bca0d958 (diff)
downloadgolang-505c19580e0f43fe5224431459cacb7c21edd93d.tar.gz
Imported Upstream version 1upstream/1
Diffstat (limited to 'src/pkg/flag')
-rw-r--r--src/pkg/flag/Makefile11
-rw-r--r--src/pkg/flag/example_test.go83
-rw-r--r--src/pkg/flag/flag.go207
-rw-r--r--src/pkg/flag/flag_test.go54
4 files changed, 274 insertions, 81 deletions
diff --git a/src/pkg/flag/Makefile b/src/pkg/flag/Makefile
deleted file mode 100644
index 3408ca474..000000000
--- a/src/pkg/flag/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright 2009 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-include ../../Make.inc
-
-TARG=flag
-GOFILES=\
- flag.go\
-
-include ../../Make.pkg
diff --git a/src/pkg/flag/example_test.go b/src/pkg/flag/example_test.go
new file mode 100644
index 000000000..04a0d20ee
--- /dev/null
+++ b/src/pkg/flag/example_test.go
@@ -0,0 +1,83 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// These examples demonstrate more intricate uses of the flag package.
+package flag_test
+
+import (
+ "errors"
+ "flag"
+ "fmt"
+ "strings"
+ "time"
+)
+
+// Example 1: A single string flag called "species" with default value "gopher".
+var species = flag.String("species", "gopher", "the species we are studying")
+
+// Example 2: Two flags sharing a variable, so we can have a shorthand.
+// The order of initialization is undefined, so make sure both use the
+// same default value. They must be set up with an init function.
+var gopherType string
+
+func init() {
+ const (
+ defaultGopher = "pocket"
+ usage = "the variety of gopher"
+ )
+ flag.StringVar(&gopherType, "gopher_type", defaultGopher, usage)
+ flag.StringVar(&gopherType, "g", defaultGopher, usage+" (shorthand)")
+}
+
+// Example 3: A user-defined flag type, a slice of durations.
+type interval []time.Duration
+
+// String is the method to format the flag's value, part of the flag.Value interface.
+// The String method's output will be used in diagnostics.
+func (i *interval) String() string {
+ return fmt.Sprint(*i)
+}
+
+// Set is the method to set the flag value, part of the flag.Value interface.
+// Set's argument is a string to be parsed to set the flag.
+// It's a comma-separated list, so we split it.
+func (i *interval) Set(value string) error {
+ // If we wanted to allow the flag to be set multiple times,
+ // accumulating values, we would delete this if statement.
+ // That would permit usages such as
+ // -deltaT 10s -deltaT 15s
+ // and other combinations.
+ if len(*i) > 0 {
+ return errors.New("interval flag already set")
+ }
+ for _, dt := range strings.Split(value, ",") {
+ duration, err := time.ParseDuration(dt)
+ if err != nil {
+ return err
+ }
+ *i = append(*i, duration)
+ }
+ return nil
+}
+
+// Define a flag to accumulate durations. Because it has a special type,
+// we need to use the Var function and therefore create the flag during
+// init.
+
+var intervalFlag interval
+
+func init() {
+ // Tie the command-line flag to the intervalFlag variable and
+ // set a usage message.
+ flag.Var(&intervalFlag, "deltaT", "comma-separated list of intervals to use between events")
+}
+
+func Example() {
+ // All the interesting pieces are with the variables declared above, but
+ // to enable the flag package to see the flags defined there, one must
+ // execute, typically at the start of main (not init!):
+ // flag.Parse()
+ // We don't run it here because this is not a main function and
+ // the testing suite has already parsed the flags.
+}
diff --git a/src/pkg/flag/flag.go b/src/pkg/flag/flag.go
index 01bbc3770..c28d0e720 100644
--- a/src/pkg/flag/flag.go
+++ b/src/pkg/flag/flag.go
@@ -49,6 +49,7 @@
Integer flags accept 1234, 0664, 0x1234 and may be negative.
Boolean flags may be 1, 0, t, f, true, false, TRUE, FALSE, True, False.
+ Duration flags accept any input valid for time.ParseDuration.
The default set of command-line flags is controlled by
top-level functions. The FlagSet type allows one to define
@@ -60,16 +61,19 @@
package flag
import (
+ "errors"
"fmt"
+ "io"
"os"
"sort"
"strconv"
+ "time"
)
// ErrHelp is the error returned if the flag -help is invoked but no such flag is defined.
-var ErrHelp = os.NewError("flag: help requested")
+var ErrHelp = errors.New("flag: help requested")
-// -- Bool Value
+// -- bool Value
type boolValue bool
func newBoolValue(val bool, p *bool) *boolValue {
@@ -77,15 +81,15 @@ func newBoolValue(val bool, p *bool) *boolValue {
return (*boolValue)(p)
}
-func (b *boolValue) Set(s string) bool {
- v, err := strconv.Atob(s)
+func (b *boolValue) Set(s string) error {
+ v, err := strconv.ParseBool(s)
*b = boolValue(v)
- return err == nil
+ return err
}
func (b *boolValue) String() string { return fmt.Sprintf("%v", *b) }
-// -- Int Value
+// -- int Value
type intValue int
func newIntValue(val int, p *int) *intValue {
@@ -93,15 +97,15 @@ func newIntValue(val int, p *int) *intValue {
return (*intValue)(p)
}
-func (i *intValue) Set(s string) bool {
- v, err := strconv.Btoi64(s, 0)
+func (i *intValue) Set(s string) error {
+ v, err := strconv.ParseInt(s, 0, 64)
*i = intValue(v)
- return err == nil
+ return err
}
func (i *intValue) String() string { return fmt.Sprintf("%v", *i) }
-// -- Int64 Value
+// -- int64 Value
type int64Value int64
func newInt64Value(val int64, p *int64) *int64Value {
@@ -109,15 +113,15 @@ func newInt64Value(val int64, p *int64) *int64Value {
return (*int64Value)(p)
}
-func (i *int64Value) Set(s string) bool {
- v, err := strconv.Btoi64(s, 0)
+func (i *int64Value) Set(s string) error {
+ v, err := strconv.ParseInt(s, 0, 64)
*i = int64Value(v)
- return err == nil
+ return err
}
func (i *int64Value) String() string { return fmt.Sprintf("%v", *i) }
-// -- Uint Value
+// -- uint Value
type uintValue uint
func newUintValue(val uint, p *uint) *uintValue {
@@ -125,10 +129,10 @@ func newUintValue(val uint, p *uint) *uintValue {
return (*uintValue)(p)
}
-func (i *uintValue) Set(s string) bool {
- v, err := strconv.Btoui64(s, 0)
+func (i *uintValue) Set(s string) error {
+ v, err := strconv.ParseUint(s, 0, 64)
*i = uintValue(v)
- return err == nil
+ return err
}
func (i *uintValue) String() string { return fmt.Sprintf("%v", *i) }
@@ -141,10 +145,10 @@ func newUint64Value(val uint64, p *uint64) *uint64Value {
return (*uint64Value)(p)
}
-func (i *uint64Value) Set(s string) bool {
- v, err := strconv.Btoui64(s, 0)
+func (i *uint64Value) Set(s string) error {
+ v, err := strconv.ParseUint(s, 0, 64)
*i = uint64Value(v)
- return err == nil
+ return err
}
func (i *uint64Value) String() string { return fmt.Sprintf("%v", *i) }
@@ -157,14 +161,14 @@ func newStringValue(val string, p *string) *stringValue {
return (*stringValue)(p)
}
-func (s *stringValue) Set(val string) bool {
+func (s *stringValue) Set(val string) error {
*s = stringValue(val)
- return true
+ return nil
}
func (s *stringValue) String() string { return fmt.Sprintf("%s", *s) }
-// -- Float64 Value
+// -- float64 Value
type float64Value float64
func newFloat64Value(val float64, p *float64) *float64Value {
@@ -172,19 +176,35 @@ func newFloat64Value(val float64, p *float64) *float64Value {
return (*float64Value)(p)
}
-func (f *float64Value) Set(s string) bool {
- v, err := strconv.Atof64(s)
+func (f *float64Value) Set(s string) error {
+ v, err := strconv.ParseFloat(s, 64)
*f = float64Value(v)
- return err == nil
+ return err
}
func (f *float64Value) String() string { return fmt.Sprintf("%v", *f) }
+// -- time.Duration Value
+type durationValue time.Duration
+
+func newDurationValue(val time.Duration, p *time.Duration) *durationValue {
+ *p = val
+ return (*durationValue)(p)
+}
+
+func (d *durationValue) Set(s string) error {
+ v, err := time.ParseDuration(s)
+ *d = durationValue(v)
+ return err
+}
+
+func (d *durationValue) String() string { return (*time.Duration)(d).String() }
+
// Value is the interface to the dynamic value stored in a flag.
// (The default value is represented as a string.)
type Value interface {
String() string
- Set(string) bool
+ Set(string) error
}
// ErrorHandling defines how to handle flag parsing errors.
@@ -204,11 +224,13 @@ type FlagSet struct {
Usage func()
name string
+ parsed bool
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
+ output io.Writer // nil means stderr; use out() accessor
}
// A Flag represents the state of a flag.
@@ -235,6 +257,19 @@ func sortFlags(flags map[string]*Flag) []*Flag {
return result
}
+func (f *FlagSet) out() io.Writer {
+ if f.output == nil {
+ return os.Stderr
+ }
+ return f.output
+}
+
+// SetOutput sets the destination for usage and error messages.
+// If output is nil, os.Stderr is used.
+func (f *FlagSet) SetOutput(output io.Writer) {
+ f.output = output
+}
+
// VisitAll visits the flags in lexicographical order, calling fn for each.
// It visits all flags, even those not set.
func (f *FlagSet) VisitAll(fn func(*Flag)) {
@@ -274,36 +309,38 @@ func Lookup(name string) *Flag {
return commandLine.formal[name]
}
-// Set sets the value of the named flag. It returns true if the set succeeded; false if
-// there is no such flag defined.
-func (f *FlagSet) Set(name, value string) bool {
+// Set sets the value of the named flag.
+func (f *FlagSet) Set(name, value string) error {
flag, ok := f.formal[name]
if !ok {
- return false
+ return fmt.Errorf("no such flag -%v", name)
}
- ok = flag.Value.Set(value)
- if !ok {
- return false
+ err := flag.Value.Set(value)
+ if err != nil {
+ return err
+ }
+ if f.actual == nil {
+ f.actual = make(map[string]*Flag)
}
f.actual[name] = flag
- return true
+ return nil
}
-// Set sets the value of the named command-line flag. It returns true if the
-// set succeeded; false if there is no such flag defined.
-func Set(name, value string) bool {
+// Set sets the value of the named command-line flag.
+func Set(name, value string) error {
return commandLine.Set(name, value)
}
-// PrintDefaults prints to standard error the default values of all defined flags in the set.
+// PrintDefaults prints, to standard error unless configured
+// otherwise, the default values of all defined flags in the set.
func (f *FlagSet) PrintDefaults() {
- f.VisitAll(func(f *Flag) {
+ f.VisitAll(func(flag *Flag) {
format := " -%s=%s: %s\n"
- if _, ok := f.Value.(*stringValue); ok {
+ if _, ok := flag.Value.(*stringValue); ok {
// put quotes on the value
format = " -%s=%q: %s\n"
}
- fmt.Fprintf(os.Stderr, format, f.Name, f.DefValue, f.Usage)
+ fmt.Fprintf(f.out(), format, flag.Name, flag.DefValue, flag.Usage)
})
}
@@ -314,14 +351,19 @@ func PrintDefaults() {
// defaultUsage is the default function to print a usage message.
func defaultUsage(f *FlagSet) {
- fmt.Fprintf(os.Stderr, "Usage of %s:\n", f.name)
+ fmt.Fprintf(f.out(), "Usage of %s:\n", f.name)
f.PrintDefaults()
}
+// NOTE: Usage is not just defaultUsage(commandLine)
+// because it serves (via godoc flag Usage) as the example
+// for how to write your own usage function.
+
// Usage prints to standard error a usage message documenting all defined command-line flags.
// The function is a variable that may be changed to point to a custom function.
var Usage = func() {
- defaultUsage(commandLine)
+ fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
+ PrintDefaults()
}
// NFlag returns the number of flags that have been set.
@@ -533,12 +575,38 @@ func (f *FlagSet) Float64(name string, value float64, usage string) *float64 {
return p
}
-// Float64 defines an int 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.
func Float64(name string, value float64, usage string) *float64 {
return commandLine.Float64(name, value, usage)
}
+// DurationVar defines a time.Duration flag with specified name, default value, and usage string.
+// The argument p points to a time.Duration variable in which to store the value of the flag.
+func (f *FlagSet) DurationVar(p *time.Duration, name string, value time.Duration, usage string) {
+ f.Var(newDurationValue(value, p), name, usage)
+}
+
+// DurationVar defines a time.Duration flag with specified name, default value, and usage string.
+// The argument p points to a time.Duration variable in which to store the value of the flag.
+func DurationVar(p *time.Duration, name string, value time.Duration, usage string) {
+ commandLine.Var(newDurationValue(value, p), name, usage)
+}
+
+// Duration defines a time.Duration flag with specified name, default value, and usage string.
+// The return value is the address of a time.Duration variable that stores the value of the flag.
+func (f *FlagSet) Duration(name string, value time.Duration, usage string) *time.Duration {
+ p := new(time.Duration)
+ f.DurationVar(p, name, value, usage)
+ return p
+}
+
+// Duration defines a time.Duration flag with specified name, default value, and usage string.
+// The return value is the address of a time.Duration variable that stores the value of the flag.
+func Duration(name string, value time.Duration, usage string) *time.Duration {
+ return commandLine.Duration(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
@@ -550,9 +618,12 @@ func (f *FlagSet) Var(value Value, name string, usage string) {
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)
+ fmt.Fprintf(f.out(), "%s flag redefined: %s\n", f.name, name)
panic("flag redefinition") // Happens only if flags are declared with identical names
}
+ if f.formal == nil {
+ f.formal = make(map[string]*Flag)
+ }
f.formal[name] = flag
}
@@ -568,9 +639,9 @@ func Var(value Value, name string, usage string) {
// 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 {
+func (f *FlagSet) failf(format string, a ...interface{}) error {
err := fmt.Errorf(format, a...)
- fmt.Fprintln(os.Stderr, err)
+ fmt.Fprintln(f.out(), err)
f.usage()
return err
}
@@ -580,13 +651,15 @@ func (f *FlagSet) failf(format string, a ...interface{}) os.Error {
func (f *FlagSet) usage() {
if f == commandLine {
Usage()
+ } else if f.Usage == nil {
+ defaultUsage(f)
} else {
f.Usage()
}
}
// parseOne parses one flag. It returns whether a flag was seen.
-func (f *FlagSet) parseOne() (bool, os.Error) {
+func (f *FlagSet) parseOne() (bool, error) {
if len(f.args) == 0 {
return false, nil
}
@@ -630,8 +703,8 @@ func (f *FlagSet) parseOne() (bool, os.Error) {
}
if fv, ok := flag.Value.(*boolValue); ok { // special case: doesn't need an arg
if has_value {
- if !fv.Set(value) {
- f.failf("invalid boolean value %q for flag: -%s", value, name)
+ if err := fv.Set(value); err != nil {
+ f.failf("invalid boolean value %q for -%s: %v", value, name, err)
}
} else {
fv.Set("true")
@@ -646,11 +719,13 @@ func (f *FlagSet) parseOne() (bool, os.Error) {
if !has_value {
return false, f.failf("flag needs an argument: -%s", name)
}
- ok = flag.Value.Set(value)
- if !ok {
- return false, f.failf("invalid value %q for flag: -%s", value, name)
+ if err := flag.Value.Set(value); err != nil {
+ return false, f.failf("invalid value %q for flag -%s: %v", value, name, err)
}
}
+ if f.actual == nil {
+ f.actual = make(map[string]*Flag)
+ }
f.actual[name] = flag
return true, nil
}
@@ -659,7 +734,8 @@ func (f *FlagSet) parseOne() (bool, os.Error) {
// include the command name. Must be called after all flags in the FlagSet
// are defined and before flags are accessed by the program.
// The return value will be ErrHelp if -help was set but not defined.
-func (f *FlagSet) Parse(arguments []string) os.Error {
+func (f *FlagSet) Parse(arguments []string) error {
+ f.parsed = true
f.args = arguments
for {
seen, err := f.parseOne()
@@ -681,6 +757,11 @@ func (f *FlagSet) Parse(arguments []string) os.Error {
return nil
}
+// Parsed reports whether f.Parse has been called.
+func (f *FlagSet) Parsed() bool {
+ return f.parsed
+}
+
// Parse parses the command-line flags from os.Args[1:]. Must be called
// after all flags are defined and before flags are accessed by the program.
func Parse() {
@@ -688,6 +769,11 @@ func Parse() {
commandLine.Parse(os.Args[1:])
}
+// Parsed returns true if the command-line flags have been parsed.
+func Parsed() bool {
+ return commandLine.Parsed()
+}
+
// The default set of command-line flags, parsed from os.Args.
var commandLine = NewFlagSet(os.Args[0], ExitOnError)
@@ -696,10 +782,15 @@ var commandLine = NewFlagSet(os.Args[0], ExitOnError)
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
}
+
+// Init sets the name and error handling property for a flag set.
+// By default, the zero FlagSet uses an empty name and the
+// ContinueOnError error handling policy.
+func (f *FlagSet) Init(name string, errorHandling ErrorHandling) {
+ f.name = name
+ f.errorHandling = errorHandling
+}
diff --git a/src/pkg/flag/flag_test.go b/src/pkg/flag/flag_test.go
index 63d0a9fc8..a9561f269 100644
--- a/src/pkg/flag/flag_test.go
+++ b/src/pkg/flag/flag_test.go
@@ -5,21 +5,25 @@
package flag_test
import (
+ "bytes"
. "flag"
"fmt"
"os"
"sort"
+ "strings"
"testing"
+ "time"
)
var (
- test_bool = Bool("test_bool", false, "bool value")
- test_int = Int("test_int", 0, "int value")
- test_int64 = Int64("test_int64", 0, "int64 value")
- test_uint = Uint("test_uint", 0, "uint value")
- test_uint64 = Uint64("test_uint64", 0, "uint64 value")
- test_string = String("test_string", "0", "string value")
- test_float64 = Float64("test_float64", 0, "float64 value")
+ test_bool = Bool("test_bool", false, "bool value")
+ test_int = Int("test_int", 0, "int value")
+ test_int64 = Int64("test_int64", 0, "int64 value")
+ test_uint = Uint("test_uint", 0, "uint value")
+ test_uint64 = Uint64("test_uint64", 0, "uint64 value")
+ test_string = String("test_string", "0", "string value")
+ test_float64 = Float64("test_float64", 0, "float64 value")
+ test_duration = Duration("test_duration", 0, "time.Duration value")
)
func boolString(s string) string {
@@ -41,6 +45,8 @@ func TestEverything(t *testing.T) {
ok = true
case f.Name == "test_bool" && f.Value.String() == boolString(desired):
ok = true
+ case f.Name == "test_duration" && f.Value.String() == desired+"s":
+ ok = true
}
if !ok {
t.Error("Visit: bad value", f.Value.String(), "for", f.Name)
@@ -48,7 +54,7 @@ func TestEverything(t *testing.T) {
}
}
VisitAll(visitor)
- if len(m) != 7 {
+ if len(m) != 8 {
t.Error("VisitAll misses some flags")
for k, v := range m {
t.Log(k, *v)
@@ -70,9 +76,10 @@ func TestEverything(t *testing.T) {
Set("test_uint64", "1")
Set("test_string", "1")
Set("test_float64", "1")
+ Set("test_duration", "1s")
desired = "1"
Visit(visitor)
- if len(m) != 7 {
+ if len(m) != 8 {
t.Error("Visit fails after set")
for k, v := range m {
t.Log(k, *v)
@@ -98,6 +105,9 @@ func TestUsage(t *testing.T) {
}
func testParse(f *FlagSet, t *testing.T) {
+ if f.Parsed() {
+ t.Error("f.Parse() = true before Parse")
+ }
boolFlag := f.Bool("bool", false, "bool value")
bool2Flag := f.Bool("bool2", false, "bool2 value")
intFlag := f.Int("int", 0, "int value")
@@ -106,6 +116,7 @@ func testParse(f *FlagSet, t *testing.T) {
uint64Flag := f.Uint64("uint64", 0, "uint64 value")
stringFlag := f.String("string", "0", "string value")
float64Flag := f.Float64("float64", 0, "float64 value")
+ durationFlag := f.Duration("duration", 5*time.Second, "time.Duration value")
extra := "one-extra-argument"
args := []string{
"-bool",
@@ -116,11 +127,15 @@ func testParse(f *FlagSet, t *testing.T) {
"--uint64", "25",
"-string", "hello",
"-float64", "2718e28",
+ "-duration", "2m",
extra,
}
if err := f.Parse(args); err != nil {
t.Fatal(err)
}
+ if !f.Parsed() {
+ t.Error("f.Parse() = false after Parse")
+ }
if *boolFlag != true {
t.Error("bool flag should be true, is ", *boolFlag)
}
@@ -145,6 +160,9 @@ func testParse(f *FlagSet, t *testing.T) {
if *float64Flag != 2718e28 {
t.Error("float64 flag should be 2718e28, is ", *float64Flag)
}
+ if *durationFlag != 2*time.Minute {
+ t.Error("duration flag should be 2m, is ", *durationFlag)
+ }
if len(f.Args()) != 1 {
t.Error("expected one argument, got", len(f.Args()))
} else if f.Args()[0] != extra {
@@ -168,13 +186,14 @@ func (f *flagVar) String() string {
return fmt.Sprint([]string(*f))
}
-func (f *flagVar) Set(value string) bool {
+func (f *flagVar) Set(value string) error {
*f = append(*f, value)
- return true
+ return nil
}
func TestUserDefined(t *testing.T) {
- flags := NewFlagSet("test", ContinueOnError)
+ var flags FlagSet
+ flags.Init("test", ContinueOnError)
var v flagVar
flags.Var(&v, "v", "usage")
if err := flags.Parse([]string{"-v", "1", "-v", "2", "-v=3"}); err != nil {
@@ -189,6 +208,17 @@ func TestUserDefined(t *testing.T) {
}
}
+func TestSetOutput(t *testing.T) {
+ var flags FlagSet
+ var buf bytes.Buffer
+ flags.SetOutput(&buf)
+ flags.Init("test", ContinueOnError)
+ flags.Parse([]string{"-unknown"})
+ if out := buf.String(); !strings.Contains(out, "-unknown") {
+ t.Logf("expected output mentioning unknown; got %q", out)
+ }
+}
+
// This tests that one can reset the flags. This still works but not well, and is
// superseded by FlagSet.
func TestChangingArgs(t *testing.T) {