diff options
author | Ondřej Surý <ondrej@sury.org> | 2011-01-17 12:40:45 +0100 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2011-01-17 12:40:45 +0100 |
commit | 3e45412327a2654a77944249962b3652e6142299 (patch) | |
tree | bc3bf69452afa055423cbe0c5cfa8ca357df6ccf /src/pkg/flag | |
parent | c533680039762cacbc37db8dc7eed074c3e497be (diff) | |
download | golang-3e45412327a2654a77944249962b3652e6142299.tar.gz |
Imported Upstream version 2011.01.12upstream/2011.01.12
Diffstat (limited to 'src/pkg/flag')
-rw-r--r-- | src/pkg/flag/Makefile | 2 | ||||
-rw-r--r-- | src/pkg/flag/export_test.go | 32 | ||||
-rw-r--r-- | src/pkg/flag/flag.go | 114 | ||||
-rw-r--r-- | src/pkg/flag/flag_test.go | 24 |
4 files changed, 99 insertions, 73 deletions
diff --git a/src/pkg/flag/Makefile b/src/pkg/flag/Makefile index 3ffbea83c..3408ca474 100644 --- a/src/pkg/flag/Makefile +++ b/src/pkg/flag/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=flag GOFILES=\ diff --git a/src/pkg/flag/export_test.go b/src/pkg/flag/export_test.go new file mode 100644 index 000000000..b5e3243b3 --- /dev/null +++ b/src/pkg/flag/export_test.go @@ -0,0 +1,32 @@ +// Copyright 2010 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. + +package flag + +import "os" + +// Additional routines compiled into the package only during testing. + +// ResetForTesting clears all flag state and sets the usage function as directed. +// After calling ResetForTesting, parse errors in flag handling will panic rather +// than exit the program. +func ResetForTesting(usage func()) { + flags = &allFlags{make(map[string]*Flag), make(map[string]*Flag), os.Args[1:]} + Usage = usage + panicOnError = true +} + +// ParseForTesting parses the flag state using the provided arguments. It +// should be called after 1) ResetForTesting and 2) setting up the new flags. +// The return value reports whether the parse was error-free. +func ParseForTesting(args []string) (result bool) { + defer func() { + if recover() != nil { + result = false + } + }() + os.Args = args + Parse() + return true +} diff --git a/src/pkg/flag/flag.go b/src/pkg/flag/flag.go index 59c33403d..04fe2fa05 100644 --- a/src/pkg/flag/flag.go +++ b/src/pkg/flag/flag.go @@ -7,7 +7,7 @@ Usage: - 1) Define flags using flag.String(), Bool(), Int(), etc. Example: + Define flags using flag.String(), Bool(), Int(), etc. Example: import "flag" var ip *int = flag.Int("flagname", 1234, "help message for flagname") If you like, you can bind the flag to a variable using the Var() functions. @@ -20,17 +20,18 @@ flag.Var(&flagVal, "name", "help message for flagname") For such flags, the default value is just the initial value of the variable. - 2) After all flags are defined, call + After all flags are defined, call flag.Parse() to parse the command line into the defined flags. - 3) Flags may then be used directly. If you're using the flags themselves, + Flags may then be used directly. If you're using the flags themselves, they are all pointers; if you bind to variables, they're values. fmt.Println("ip has value ", *ip); fmt.Println("flagvar has value ", flagvar); - 4) After parsing, flag.Arg(i) is the i'th argument after the flags. - Args are indexed from 0 up to flag.NArg(). + After parsing, the arguments after the flag are available as the + slice flag.Args() or individually as flag.Arg(i). + The arguments are indexed from 0 up to flag.NArg(). Command line flag syntax: -flag @@ -48,6 +49,19 @@ Integer flags accept 1234, 0664, 0x1234 and may be negative. 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 + os.Args. This makes it possible to implement command lines with + subcommands that enable additional flags, as in: + + flag.Bool(...) // global options + flag.Parse() // parse leading command + subcmd := flag.Args(0) + switch subcmd { + // add per-subcommand options + } + os.Args = flag.Args() + flag.Parse() */ package flag @@ -200,9 +214,9 @@ type Flag struct { } type allFlags struct { - actual map[string]*Flag - formal map[string]*Flag - first_arg int // 0 is the program name, 1 is first arg + actual map[string]*Flag + formal map[string]*Flag + args []string // arguments after flags } var flags *allFlags @@ -275,18 +289,17 @@ func NFlag() int { return len(flags.actual) } // Arg returns the i'th command-line argument. Arg(0) is the first remaining argument // after flags have been processed. func Arg(i int) string { - i += flags.first_arg - if i < 0 || i >= len(os.Args) { + if i < 0 || i >= len(flags.args) { return "" } - return os.Args[i] + return flags.args[i] } // NArg is the number of arguments remaining after flags have been processed. -func NArg() int { return len(os.Args) - flags.first_arg } +func NArg() int { return len(flags.args) } // Args returns the non-flag command-line arguments. -func Args() []string { return os.Args[flags.first_arg:] } +func Args() []string { return flags.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. @@ -414,23 +427,20 @@ func Var(value Value, name string, usage string) { } -func (f *allFlags) parseOne(index int) (ok bool, next int) { - s := os.Args[index] - f.first_arg = index // until proven otherwise - if len(s) == 0 { - return false, -1 +func (f *allFlags) parseOne() (ok bool) { + if len(f.args) == 0 { + return false } - if s[0] != '-' { - return false, -1 + s := f.args[0] + if len(s) == 0 || s[0] != '-' || len(s) == 1 { + return false } num_minuses := 1 - if len(s) == 1 { - return false, index - } if s[1] == '-' { num_minuses++ if len(s) == 2 { // "--" terminates the flags - return false, index + 1 + f.args = f.args[1:] + return false } } name := s[num_minuses:] @@ -440,6 +450,7 @@ func (f *allFlags) parseOne(index int) (ok bool, next int) { } // it's a flag. does it have an argument? + f.args = f.args[1:] has_value := false value := "" for i := 1; i < len(name); i++ { // equals cannot be first @@ -456,22 +467,21 @@ func (f *allFlags) parseOne(index int) (ok bool, next int) { fmt.Fprintf(os.Stderr, "flag provided but not defined: -%s\n", name) fail() } - if f, 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 !f.Set(value) { - fmt.Fprintf(os.Stderr, "invalid boolean value %t for flag: -%s\n", value, name) + if !fv.Set(value) { + fmt.Fprintf(os.Stderr, "invalid boolean value %q for flag: -%s\n", value, name) fail() } } else { - f.Set("true") + fv.Set("true") } } else { // It must have a value, which might be the next argument. - if !has_value && index < len(os.Args)-1 { + if !has_value && len(f.args) > 0 { // value is the next arg has_value = true - index++ - value = os.Args[index] + value, f.args = f.args[0], f.args[1:] } if !has_value { fmt.Fprintf(os.Stderr, "flag needs an argument: -%s\n", name) @@ -479,54 +489,22 @@ func (f *allFlags) parseOne(index int) (ok bool, next int) { } ok = flag.Value.Set(value) if !ok { - fmt.Fprintf(os.Stderr, "invalid value %s for flag: -%s\n", value, name) + fmt.Fprintf(os.Stderr, "invalid value %q for flag: -%s\n", value, name) fail() } } flags.actual[name] = flag - return true, index + 1 + return true } // Parse parses the command-line flags. Must be called after all flags are defined // and before any are accessed by the program. func Parse() { - for i := 1; i < len(os.Args); { - ok, next := flags.parseOne(i) - if next > 0 { - flags.first_arg = next - i = next - } - if !ok { - break - } + flags.args = os.Args[1:] + for flags.parseOne() { } } -// ResetForTesting clears all flag state and sets the usage function as directed. -// After calling ResetForTesting, parse errors in flag handling will panic rather -// than exit the program. -// For testing only! -func ResetForTesting(usage func()) { - flags = &allFlags{make(map[string]*Flag), make(map[string]*Flag), 1} - Usage = usage - panicOnError = true -} - -// ParseForTesting parses the flag state using the provided arguments. It -// should be called after 1) ResetForTesting and 2) setting up the new flags. -// The return value reports whether the parse was error-free. -// For testing only! -func ParseForTesting(args []string) (result bool) { - defer func() { - if recover() != nil { - result = false - } - }() - os.Args = args - Parse() - return true -} - func init() { - flags = &allFlags{make(map[string]*Flag), make(map[string]*Flag), 1} + flags = &allFlags{make(map[string]*Flag), make(map[string]*Flag), os.Args[1:]} } diff --git a/src/pkg/flag/flag_test.go b/src/pkg/flag/flag_test.go index 83bf7eebf..4ebb73805 100644 --- a/src/pkg/flag/flag_test.go +++ b/src/pkg/flag/flag_test.go @@ -7,6 +7,7 @@ package flag_test import ( . "flag" "fmt" + "os" "testing" ) @@ -161,10 +162,7 @@ func (f *flagVar) String() string { } func (f *flagVar) Set(value string) bool { - n := make(flagVar, len(*f)+1) - copy(n, *f) - *f = n - (*f)[len(*f)-1] = value + *f = append(*f, value) return true } @@ -183,3 +181,21 @@ func TestUserDefined(t *testing.T) { t.Errorf("expected value %q got %q", expect, v.String()) } } + +func TestChangingArgs(t *testing.T) { + ResetForTesting(func() { t.Fatal("bad parse") }) + oldArgs := os.Args + defer func() { os.Args = oldArgs }() + os.Args = []string{"cmd", "-before", "subcmd", "-after", "args"} + before := Bool("before", false, "") + Parse() + cmd := Arg(0) + os.Args = Args() + after := Bool("after", false, "") + Parse() + args := Args() + + if !*before || cmd != "subcmd" || !*after || len(args) != 1 || args[0] != "args" { + t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args) + } +} |