|
|
@@ -16,9 +16,9 @@ pflag is a drop-in replacement of Go's native flag package. If you import
|
|
|
pflag under the name "flag" then all code should continue to function
|
|
|
with no changes.
|
|
|
|
|
|
- import flag "github.com/ogier/pflag"
|
|
|
+ import flag "github.com/spf13/pflag"
|
|
|
|
|
|
- There is one exception to this: if you directly instantiate the Flag struct
|
|
|
+There is one exception to this: if you directly instantiate the Flag struct
|
|
|
there is one more field "Shorthand" that you will need to set.
|
|
|
Most code never instantiates this struct directly, and instead uses
|
|
|
functions such as String(), BoolVar(), and Var(), and is therefore
|
|
|
@@ -134,14 +134,21 @@ type FlagSet struct {
|
|
|
// a custom error handler.
|
|
|
Usage func()
|
|
|
|
|
|
+ // SortFlags is used to indicate, if user wants to have sorted flags in
|
|
|
+ // help/usage messages.
|
|
|
+ SortFlags bool
|
|
|
+
|
|
|
name string
|
|
|
parsed bool
|
|
|
actual map[NormalizedName]*Flag
|
|
|
+ orderedActual []*Flag
|
|
|
+ sortedActual []*Flag
|
|
|
formal map[NormalizedName]*Flag
|
|
|
+ orderedFormal []*Flag
|
|
|
+ sortedFormal []*Flag
|
|
|
shorthands map[byte]*Flag
|
|
|
args []string // arguments after flags
|
|
|
argsLenAtDash int // len(args) when a '--' was located when parsing, or -1 if no --
|
|
|
- exitOnError bool // does the program exit if there's an error?
|
|
|
errorHandling ErrorHandling
|
|
|
output io.Writer // nil means stderr; use out() accessor
|
|
|
interspersed bool // allow interspersed option/non-option args
|
|
|
@@ -156,7 +163,7 @@ type Flag struct {
|
|
|
Value Value // value as set
|
|
|
DefValue string // default value (as text); for usage message
|
|
|
Changed bool // If the user set the value (or if left to default)
|
|
|
- NoOptDefVal string //default value (as text); if the flag is on the command line without any options
|
|
|
+ NoOptDefVal string // default value (as text); if the flag is on the command line without any options
|
|
|
Deprecated string // If this flag is deprecated, this string is the new or now thing to use
|
|
|
Hidden bool // used by cobra.Command to allow flags to be hidden from help/usage text
|
|
|
ShorthandDeprecated string // If the shorthand of this flag is deprecated, this string is the new or now thing to use
|
|
|
@@ -194,11 +201,13 @@ func sortFlags(flags map[NormalizedName]*Flag) []*Flag {
|
|
|
// "--getUrl" which may also be translated to "geturl" and everything will work.
|
|
|
func (f *FlagSet) SetNormalizeFunc(n func(f *FlagSet, name string) NormalizedName) {
|
|
|
f.normalizeNameFunc = n
|
|
|
- for k, v := range f.formal {
|
|
|
- delete(f.formal, k)
|
|
|
- nname := f.normalizeFlagName(string(k))
|
|
|
- f.formal[nname] = v
|
|
|
+ f.sortedFormal = f.sortedFormal[:0]
|
|
|
+ for k, v := range f.orderedFormal {
|
|
|
+ delete(f.formal, NormalizedName(v.Name))
|
|
|
+ nname := f.normalizeFlagName(v.Name)
|
|
|
v.Name = string(nname)
|
|
|
+ f.formal[nname] = v
|
|
|
+ f.orderedFormal[k] = v
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -229,10 +238,25 @@ func (f *FlagSet) SetOutput(output io.Writer) {
|
|
|
f.output = output
|
|
|
}
|
|
|
|
|
|
-// VisitAll visits the flags in lexicographical order, calling fn for each.
|
|
|
+// VisitAll visits the flags in lexicographical order or
|
|
|
+// in primordial order if f.SortFlags is false, calling fn for each.
|
|
|
// It visits all flags, even those not set.
|
|
|
func (f *FlagSet) VisitAll(fn func(*Flag)) {
|
|
|
- for _, flag := range sortFlags(f.formal) {
|
|
|
+ if len(f.formal) == 0 {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ var flags []*Flag
|
|
|
+ if f.SortFlags {
|
|
|
+ if len(f.formal) != len(f.sortedFormal) {
|
|
|
+ f.sortedFormal = sortFlags(f.formal)
|
|
|
+ }
|
|
|
+ flags = f.sortedFormal
|
|
|
+ } else {
|
|
|
+ flags = f.orderedFormal
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, flag := range flags {
|
|
|
fn(flag)
|
|
|
}
|
|
|
}
|
|
|
@@ -242,22 +266,50 @@ func (f *FlagSet) HasFlags() bool {
|
|
|
return len(f.formal) > 0
|
|
|
}
|
|
|
|
|
|
-// VisitAll visits the command-line flags in lexicographical order, calling
|
|
|
-// fn for each. It visits all flags, even those not set.
|
|
|
+// HasAvailableFlags returns a bool to indicate if the FlagSet has any flags
|
|
|
+// definied that are not hidden or deprecated.
|
|
|
+func (f *FlagSet) HasAvailableFlags() bool {
|
|
|
+ for _, flag := range f.formal {
|
|
|
+ if !flag.Hidden && len(flag.Deprecated) == 0 {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|
|
|
+
|
|
|
+// VisitAll visits the command-line flags in lexicographical order or
|
|
|
+// in primordial order if f.SortFlags is false, 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 or
|
|
|
+// in primordial order if f.SortFlags is false, calling fn for each.
|
|
|
// It visits only those flags that have been set.
|
|
|
func (f *FlagSet) Visit(fn func(*Flag)) {
|
|
|
- for _, flag := range sortFlags(f.actual) {
|
|
|
+ if len(f.actual) == 0 {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ var flags []*Flag
|
|
|
+ if f.SortFlags {
|
|
|
+ if len(f.actual) != len(f.sortedActual) {
|
|
|
+ f.sortedActual = sortFlags(f.actual)
|
|
|
+ }
|
|
|
+ flags = f.sortedActual
|
|
|
+ } else {
|
|
|
+ flags = f.orderedActual
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, flag := range flags {
|
|
|
fn(flag)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// Visit visits the command-line flags in lexicographical order, calling fn
|
|
|
-// for each. It visits only those flags that have been set.
|
|
|
+// Visit visits the command-line flags in lexicographical order or
|
|
|
+// in primordial order if f.SortFlags is false, calling fn for each.
|
|
|
+// It visits only those flags that have been set.
|
|
|
func Visit(fn func(*Flag)) {
|
|
|
CommandLine.Visit(fn)
|
|
|
}
|
|
|
@@ -267,6 +319,22 @@ func (f *FlagSet) Lookup(name string) *Flag {
|
|
|
return f.lookup(f.normalizeFlagName(name))
|
|
|
}
|
|
|
|
|
|
+// ShorthandLookup returns the Flag structure of the short handed flag,
|
|
|
+// returning nil if none exists.
|
|
|
+// It panics, if len(name) > 1.
|
|
|
+func (f *FlagSet) ShorthandLookup(name string) *Flag {
|
|
|
+ if name == "" {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ if len(name) > 1 {
|
|
|
+ msg := fmt.Sprintf("can not look up shorthand which is more than one ASCII character: %q", name)
|
|
|
+ fmt.Fprintf(f.out(), msg)
|
|
|
+ panic(msg)
|
|
|
+ }
|
|
|
+ c := name[0]
|
|
|
+ return f.shorthands[c]
|
|
|
+}
|
|
|
+
|
|
|
// lookup returns the Flag structure of the named flag, returning nil if none exists.
|
|
|
func (f *FlagSet) lookup(name NormalizedName) *Flag {
|
|
|
return f.formal[name]
|
|
|
@@ -308,7 +376,7 @@ func (f *FlagSet) MarkDeprecated(name string, usageMessage string) error {
|
|
|
if flag == nil {
|
|
|
return fmt.Errorf("flag %q does not exist", name)
|
|
|
}
|
|
|
- if len(usageMessage) == 0 {
|
|
|
+ if usageMessage == "" {
|
|
|
return fmt.Errorf("deprecated message for flag %q must be set", name)
|
|
|
}
|
|
|
flag.Deprecated = usageMessage
|
|
|
@@ -323,7 +391,7 @@ func (f *FlagSet) MarkShorthandDeprecated(name string, usageMessage string) erro
|
|
|
if flag == nil {
|
|
|
return fmt.Errorf("flag %q does not exist", name)
|
|
|
}
|
|
|
- if len(usageMessage) == 0 {
|
|
|
+ if usageMessage == "" {
|
|
|
return fmt.Errorf("deprecated message for flag %q must be set", name)
|
|
|
}
|
|
|
flag.ShorthandDeprecated = usageMessage
|
|
|
@@ -347,6 +415,12 @@ func Lookup(name string) *Flag {
|
|
|
return CommandLine.Lookup(name)
|
|
|
}
|
|
|
|
|
|
+// ShorthandLookup returns the Flag structure of the short handed flag,
|
|
|
+// returning nil if none exists.
|
|
|
+func ShorthandLookup(name string) *Flag {
|
|
|
+ return CommandLine.ShorthandLookup(name)
|
|
|
+}
|
|
|
+
|
|
|
// Set sets the value of the named flag.
|
|
|
func (f *FlagSet) Set(name, value string) error {
|
|
|
normalName := f.normalizeFlagName(name)
|
|
|
@@ -354,17 +428,28 @@ func (f *FlagSet) Set(name, value string) error {
|
|
|
if !ok {
|
|
|
return fmt.Errorf("no such flag -%v", name)
|
|
|
}
|
|
|
+
|
|
|
err := flag.Value.Set(value)
|
|
|
if err != nil {
|
|
|
- return err
|
|
|
+ var flagName string
|
|
|
+ if flag.Shorthand != "" && flag.ShorthandDeprecated == "" {
|
|
|
+ flagName = fmt.Sprintf("-%s, --%s", flag.Shorthand, flag.Name)
|
|
|
+ } else {
|
|
|
+ flagName = fmt.Sprintf("--%s", flag.Name)
|
|
|
+ }
|
|
|
+ return fmt.Errorf("invalid argument %q for %q flag: %v", value, flagName, err)
|
|
|
}
|
|
|
+
|
|
|
if f.actual == nil {
|
|
|
f.actual = make(map[NormalizedName]*Flag)
|
|
|
}
|
|
|
f.actual[normalName] = flag
|
|
|
+ f.orderedActual = append(f.orderedActual, flag)
|
|
|
+
|
|
|
flag.Changed = true
|
|
|
- if len(flag.Deprecated) > 0 {
|
|
|
- fmt.Fprintf(os.Stderr, "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated)
|
|
|
+
|
|
|
+ if flag.Deprecated != "" {
|
|
|
+ fmt.Fprintf(f.out(), "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated)
|
|
|
}
|
|
|
return nil
|
|
|
}
|
|
|
@@ -405,45 +490,214 @@ func Set(name, value string) error {
|
|
|
// otherwise, the default values of all defined flags in the set.
|
|
|
func (f *FlagSet) PrintDefaults() {
|
|
|
usages := f.FlagUsages()
|
|
|
- fmt.Fprintf(f.out(), "%s", usages)
|
|
|
+ fmt.Fprint(f.out(), usages)
|
|
|
}
|
|
|
|
|
|
-// FlagUsages Returns a string containing the usage information for all flags in
|
|
|
-// the FlagSet
|
|
|
-func (f *FlagSet) FlagUsages() string {
|
|
|
- x := new(bytes.Buffer)
|
|
|
+// defaultIsZeroValue returns true if the default value for this flag represents
|
|
|
+// a zero value.
|
|
|
+func (f *Flag) defaultIsZeroValue() bool {
|
|
|
+ switch f.Value.(type) {
|
|
|
+ case boolFlag:
|
|
|
+ return f.DefValue == "false"
|
|
|
+ case *durationValue:
|
|
|
+ // Beginning in Go 1.7, duration zero values are "0s"
|
|
|
+ return f.DefValue == "0" || f.DefValue == "0s"
|
|
|
+ case *intValue, *int8Value, *int32Value, *int64Value, *uintValue, *uint8Value, *uint16Value, *uint32Value, *uint64Value, *countValue, *float32Value, *float64Value:
|
|
|
+ return f.DefValue == "0"
|
|
|
+ case *stringValue:
|
|
|
+ return f.DefValue == ""
|
|
|
+ case *ipValue, *ipMaskValue, *ipNetValue:
|
|
|
+ return f.DefValue == "<nil>"
|
|
|
+ case *intSliceValue, *stringSliceValue, *stringArrayValue:
|
|
|
+ return f.DefValue == "[]"
|
|
|
+ default:
|
|
|
+ switch f.Value.String() {
|
|
|
+ case "false":
|
|
|
+ return true
|
|
|
+ case "<nil>":
|
|
|
+ return true
|
|
|
+ case "":
|
|
|
+ return true
|
|
|
+ case "0":
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ return false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// UnquoteUsage extracts a back-quoted name from the usage
|
|
|
+// string for a flag and returns it and the un-quoted usage.
|
|
|
+// Given "a `name` to show" it returns ("name", "a name to show").
|
|
|
+// If there are no back quotes, the name is an educated guess of the
|
|
|
+// type of the flag's value, or the empty string if the flag is boolean.
|
|
|
+func UnquoteUsage(flag *Flag) (name string, usage string) {
|
|
|
+ // Look for a back-quoted name, but avoid the strings package.
|
|
|
+ usage = flag.Usage
|
|
|
+ for i := 0; i < len(usage); i++ {
|
|
|
+ if usage[i] == '`' {
|
|
|
+ for j := i + 1; j < len(usage); j++ {
|
|
|
+ if usage[j] == '`' {
|
|
|
+ name = usage[i+1 : j]
|
|
|
+ usage = usage[:i] + name + usage[j+1:]
|
|
|
+ return name, usage
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break // Only one back quote; use type name.
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ name = flag.Value.Type()
|
|
|
+ switch name {
|
|
|
+ case "bool":
|
|
|
+ name = ""
|
|
|
+ case "float64":
|
|
|
+ name = "float"
|
|
|
+ case "int64":
|
|
|
+ name = "int"
|
|
|
+ case "uint64":
|
|
|
+ name = "uint"
|
|
|
+ }
|
|
|
+
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// Splits the string `s` on whitespace into an initial substring up to
|
|
|
+// `i` runes in length and the remainder. Will go `slop` over `i` if
|
|
|
+// that encompasses the entire string (which allows the caller to
|
|
|
+// avoid short orphan words on the final line).
|
|
|
+func wrapN(i, slop int, s string) (string, string) {
|
|
|
+ if i+slop > len(s) {
|
|
|
+ return s, ""
|
|
|
+ }
|
|
|
+
|
|
|
+ w := strings.LastIndexAny(s[:i], " \t")
|
|
|
+ if w <= 0 {
|
|
|
+ return s, ""
|
|
|
+ }
|
|
|
+
|
|
|
+ return s[:w], s[w+1:]
|
|
|
+}
|
|
|
+
|
|
|
+// Wraps the string `s` to a maximum width `w` with leading indent
|
|
|
+// `i`. The first line is not indented (this is assumed to be done by
|
|
|
+// caller). Pass `w` == 0 to do no wrapping
|
|
|
+func wrap(i, w int, s string) string {
|
|
|
+ if w == 0 {
|
|
|
+ return s
|
|
|
+ }
|
|
|
+
|
|
|
+ // space between indent i and end of line width w into which
|
|
|
+ // we should wrap the text.
|
|
|
+ wrap := w - i
|
|
|
+
|
|
|
+ var r, l string
|
|
|
+
|
|
|
+ // Not enough space for sensible wrapping. Wrap as a block on
|
|
|
+ // the next line instead.
|
|
|
+ if wrap < 24 {
|
|
|
+ i = 16
|
|
|
+ wrap = w - i
|
|
|
+ r += "\n" + strings.Repeat(" ", i)
|
|
|
+ }
|
|
|
+ // If still not enough space then don't even try to wrap.
|
|
|
+ if wrap < 24 {
|
|
|
+ return s
|
|
|
+ }
|
|
|
+
|
|
|
+ // Try to avoid short orphan words on the final line, by
|
|
|
+ // allowing wrapN to go a bit over if that would fit in the
|
|
|
+ // remainder of the line.
|
|
|
+ slop := 5
|
|
|
+ wrap = wrap - slop
|
|
|
+
|
|
|
+ // Handle first line, which is indented by the caller (or the
|
|
|
+ // special case above)
|
|
|
+ l, s = wrapN(wrap, slop, s)
|
|
|
+ r = r + l
|
|
|
+
|
|
|
+ // Now wrap the rest
|
|
|
+ for s != "" {
|
|
|
+ var t string
|
|
|
+
|
|
|
+ t, s = wrapN(wrap, slop, s)
|
|
|
+ r = r + "\n" + strings.Repeat(" ", i) + t
|
|
|
+ }
|
|
|
|
|
|
+ return r
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+// FlagUsagesWrapped returns a string containing the usage information
|
|
|
+// for all flags in the FlagSet. Wrapped to `cols` columns (0 for no
|
|
|
+// wrapping)
|
|
|
+func (f *FlagSet) FlagUsagesWrapped(cols int) string {
|
|
|
+ buf := new(bytes.Buffer)
|
|
|
+
|
|
|
+ lines := make([]string, 0, len(f.formal))
|
|
|
+
|
|
|
+ maxlen := 0
|
|
|
f.VisitAll(func(flag *Flag) {
|
|
|
- if len(flag.Deprecated) > 0 || flag.Hidden {
|
|
|
+ if flag.Deprecated != "" || flag.Hidden {
|
|
|
return
|
|
|
}
|
|
|
- format := ""
|
|
|
- if len(flag.Shorthand) > 0 && len(flag.ShorthandDeprecated) == 0 {
|
|
|
- format = " -%s, --%s"
|
|
|
+
|
|
|
+ line := ""
|
|
|
+ if flag.Shorthand != "" && flag.ShorthandDeprecated == "" {
|
|
|
+ line = fmt.Sprintf(" -%s, --%s", flag.Shorthand, flag.Name)
|
|
|
} else {
|
|
|
- format = " %s --%s"
|
|
|
+ line = fmt.Sprintf(" --%s", flag.Name)
|
|
|
}
|
|
|
- if len(flag.NoOptDefVal) > 0 {
|
|
|
- format = format + "["
|
|
|
+
|
|
|
+ varname, usage := UnquoteUsage(flag)
|
|
|
+ if varname != "" {
|
|
|
+ line += " " + varname
|
|
|
}
|
|
|
- if flag.Value.Type() == "string" {
|
|
|
- // put quotes on the value
|
|
|
- format = format + "=%q"
|
|
|
- } else {
|
|
|
- format = format + "=%s"
|
|
|
+ if flag.NoOptDefVal != "" {
|
|
|
+ switch flag.Value.Type() {
|
|
|
+ case "string":
|
|
|
+ line += fmt.Sprintf("[=\"%s\"]", flag.NoOptDefVal)
|
|
|
+ case "bool":
|
|
|
+ if flag.NoOptDefVal != "true" {
|
|
|
+ line += fmt.Sprintf("[=%s]", flag.NoOptDefVal)
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ line += fmt.Sprintf("[=%s]", flag.NoOptDefVal)
|
|
|
+ }
|
|
|
}
|
|
|
- if len(flag.NoOptDefVal) > 0 {
|
|
|
- format = format + "]"
|
|
|
+
|
|
|
+ // This special character will be replaced with spacing once the
|
|
|
+ // correct alignment is calculated
|
|
|
+ line += "\x00"
|
|
|
+ if len(line) > maxlen {
|
|
|
+ maxlen = len(line)
|
|
|
}
|
|
|
- format = format + ": %s\n"
|
|
|
- shorthand := flag.Shorthand
|
|
|
- if len(flag.ShorthandDeprecated) > 0 {
|
|
|
- shorthand = ""
|
|
|
+
|
|
|
+ line += usage
|
|
|
+ if !flag.defaultIsZeroValue() {
|
|
|
+ if flag.Value.Type() == "string" {
|
|
|
+ line += fmt.Sprintf(" (default %q)", flag.DefValue)
|
|
|
+ } else {
|
|
|
+ line += fmt.Sprintf(" (default %s)", flag.DefValue)
|
|
|
+ }
|
|
|
}
|
|
|
- fmt.Fprintf(x, format, shorthand, flag.Name, flag.DefValue, flag.Usage)
|
|
|
+
|
|
|
+ lines = append(lines, line)
|
|
|
})
|
|
|
|
|
|
- return x.String()
|
|
|
+ for _, line := range lines {
|
|
|
+ sidx := strings.Index(line, "\x00")
|
|
|
+ spacing := strings.Repeat(" ", maxlen-sidx)
|
|
|
+ // maxlen + 2 comes from + 1 for the \x00 and + 1 for the (deliberate) off-by-one in maxlen-sidx
|
|
|
+ fmt.Fprintln(buf, line[:sidx], spacing, wrap(maxlen+2, cols, line[sidx+1:]))
|
|
|
+ }
|
|
|
+
|
|
|
+ return buf.String()
|
|
|
+}
|
|
|
+
|
|
|
+// FlagUsages returns a string containing the usage information for all flags in
|
|
|
+// the FlagSet
|
|
|
+func (f *FlagSet) FlagUsages() string {
|
|
|
+ return f.FlagUsagesWrapped(0)
|
|
|
}
|
|
|
|
|
|
// PrintDefaults prints to standard error the default values of all defined command-line flags.
|
|
|
@@ -463,6 +717,8 @@ func defaultUsage(f *FlagSet) {
|
|
|
|
|
|
// 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.
|
|
|
+// By default it prints a simple header and calls PrintDefaults; for details about the
|
|
|
+// format of the output and how to control it, see the documentation for PrintDefaults.
|
|
|
var Usage = func() {
|
|
|
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
|
|
|
PrintDefaults()
|
|
|
@@ -527,16 +783,15 @@ func (f *FlagSet) VarPF(value Value, name, shorthand, usage string) *Flag {
|
|
|
|
|
|
// VarP is like Var, but accepts a shorthand letter that can be used after a single dash.
|
|
|
func (f *FlagSet) VarP(value Value, name, shorthand, usage string) {
|
|
|
- _ = f.VarPF(value, name, shorthand, usage)
|
|
|
+ f.VarPF(value, name, shorthand, usage)
|
|
|
}
|
|
|
|
|
|
// AddFlag will add the flag to the FlagSet
|
|
|
func (f *FlagSet) AddFlag(flag *Flag) {
|
|
|
- // Call normalizeFlagName function only once
|
|
|
normalizedFlagName := f.normalizeFlagName(flag.Name)
|
|
|
|
|
|
- _, alreadythere := f.formal[normalizedFlagName]
|
|
|
- if alreadythere {
|
|
|
+ _, alreadyThere := f.formal[normalizedFlagName]
|
|
|
+ if alreadyThere {
|
|
|
msg := fmt.Sprintf("%s flag redefined: %s", f.name, flag.Name)
|
|
|
fmt.Fprintln(f.out(), msg)
|
|
|
panic(msg) // Happens only if flags are declared with identical names
|
|
|
@@ -547,28 +802,31 @@ func (f *FlagSet) AddFlag(flag *Flag) {
|
|
|
|
|
|
flag.Name = string(normalizedFlagName)
|
|
|
f.formal[normalizedFlagName] = flag
|
|
|
+ f.orderedFormal = append(f.orderedFormal, flag)
|
|
|
|
|
|
- if len(flag.Shorthand) == 0 {
|
|
|
+ if flag.Shorthand == "" {
|
|
|
return
|
|
|
}
|
|
|
if len(flag.Shorthand) > 1 {
|
|
|
- fmt.Fprintf(f.out(), "%s shorthand more than ASCII character: %s\n", f.name, flag.Shorthand)
|
|
|
- panic("shorthand is more than one character")
|
|
|
+ msg := fmt.Sprintf("%q shorthand is more than one ASCII character", flag.Shorthand)
|
|
|
+ fmt.Fprintf(f.out(), msg)
|
|
|
+ panic(msg)
|
|
|
}
|
|
|
if f.shorthands == nil {
|
|
|
f.shorthands = make(map[byte]*Flag)
|
|
|
}
|
|
|
c := flag.Shorthand[0]
|
|
|
- old, alreadythere := f.shorthands[c]
|
|
|
- if alreadythere {
|
|
|
- fmt.Fprintf(f.out(), "%s shorthand reused: %q for %s already used for %s\n", f.name, c, flag.Name, old.Name)
|
|
|
- panic("shorthand redefinition")
|
|
|
+ used, alreadyThere := f.shorthands[c]
|
|
|
+ if alreadyThere {
|
|
|
+ msg := fmt.Sprintf("unable to redefine %q shorthand in %q flagset: it's already used for %q flag", c, f.name, used.Name)
|
|
|
+ fmt.Fprintf(f.out(), msg)
|
|
|
+ panic(msg)
|
|
|
}
|
|
|
f.shorthands[c] = flag
|
|
|
}
|
|
|
|
|
|
// AddFlagSet adds one FlagSet to another. If a flag is already present in f
|
|
|
-// the flag from newSet will be ignored
|
|
|
+// the flag from newSet will be ignored.
|
|
|
func (f *FlagSet) AddFlagSet(newSet *FlagSet) {
|
|
|
if newSet == nil {
|
|
|
return
|
|
|
@@ -616,45 +874,18 @@ func (f *FlagSet) usage() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func (f *FlagSet) setFlag(flag *Flag, value string, origArg string) error {
|
|
|
- if err := flag.Value.Set(value); err != nil {
|
|
|
- return f.failf("invalid argument %q for %s: %v", value, origArg, err)
|
|
|
- }
|
|
|
- // mark as visited for Visit()
|
|
|
- if f.actual == nil {
|
|
|
- f.actual = make(map[NormalizedName]*Flag)
|
|
|
- }
|
|
|
- f.actual[f.normalizeFlagName(flag.Name)] = flag
|
|
|
- flag.Changed = true
|
|
|
- if len(flag.Deprecated) > 0 {
|
|
|
- fmt.Fprintf(os.Stderr, "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated)
|
|
|
- }
|
|
|
- if len(flag.ShorthandDeprecated) > 0 && containsShorthand(origArg, flag.Shorthand) {
|
|
|
- fmt.Fprintf(os.Stderr, "Flag shorthand -%s has been deprecated, %s\n", flag.Shorthand, flag.ShorthandDeprecated)
|
|
|
- }
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-func containsShorthand(arg, shorthand string) bool {
|
|
|
- // filter out flags --<flag_name>
|
|
|
- if strings.HasPrefix(arg, "-") {
|
|
|
- return false
|
|
|
- }
|
|
|
- arg = strings.SplitN(arg, "=", 2)[0]
|
|
|
- return strings.Contains(arg, shorthand)
|
|
|
-}
|
|
|
-
|
|
|
-func (f *FlagSet) parseLongArg(s string, args []string) (a []string, err error) {
|
|
|
+func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []string, err error) {
|
|
|
a = args
|
|
|
name := s[2:]
|
|
|
if len(name) == 0 || name[0] == '-' || name[0] == '=' {
|
|
|
err = f.failf("bad flag syntax: %s", s)
|
|
|
return
|
|
|
}
|
|
|
+
|
|
|
split := strings.SplitN(name, "=", 2)
|
|
|
name = split[0]
|
|
|
- flag, alreadythere := f.formal[f.normalizeFlagName(name)]
|
|
|
- if !alreadythere {
|
|
|
+ flag, exists := f.formal[f.normalizeFlagName(name)]
|
|
|
+ if !exists {
|
|
|
if name == "help" { // special case for nice help message.
|
|
|
f.usage()
|
|
|
return a, ErrHelp
|
|
|
@@ -662,11 +893,12 @@ func (f *FlagSet) parseLongArg(s string, args []string) (a []string, err error)
|
|
|
err = f.failf("unknown flag: --%s", name)
|
|
|
return
|
|
|
}
|
|
|
+
|
|
|
var value string
|
|
|
if len(split) == 2 {
|
|
|
// '--flag=arg'
|
|
|
value = split[1]
|
|
|
- } else if len(flag.NoOptDefVal) > 0 {
|
|
|
+ } else if flag.NoOptDefVal != "" {
|
|
|
// '--flag' (arg was optional)
|
|
|
value = flag.NoOptDefVal
|
|
|
} else if len(a) > 0 {
|
|
|
@@ -678,52 +910,68 @@ func (f *FlagSet) parseLongArg(s string, args []string) (a []string, err error)
|
|
|
err = f.failf("flag needs an argument: %s", s)
|
|
|
return
|
|
|
}
|
|
|
- err = f.setFlag(flag, value, s)
|
|
|
+
|
|
|
+ err = fn(flag, value)
|
|
|
return
|
|
|
}
|
|
|
|
|
|
-func (f *FlagSet) parseSingleShortArg(shorthands string, args []string) (outShorts string, outArgs []string, err error) {
|
|
|
+func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parseFunc) (outShorts string, outArgs []string, err error) {
|
|
|
+ if strings.HasPrefix(shorthands, "test.") {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
outArgs = args
|
|
|
outShorts = shorthands[1:]
|
|
|
c := shorthands[0]
|
|
|
|
|
|
- flag, alreadythere := f.shorthands[c]
|
|
|
- if !alreadythere {
|
|
|
+ flag, exists := f.shorthands[c]
|
|
|
+ if !exists {
|
|
|
if c == 'h' { // special case for nice help message.
|
|
|
f.usage()
|
|
|
err = ErrHelp
|
|
|
return
|
|
|
}
|
|
|
- //TODO continue on error
|
|
|
err = f.failf("unknown shorthand flag: %q in -%s", c, shorthands)
|
|
|
return
|
|
|
}
|
|
|
+
|
|
|
var value string
|
|
|
if len(shorthands) > 2 && shorthands[1] == '=' {
|
|
|
+ // '-f=arg'
|
|
|
value = shorthands[2:]
|
|
|
outShorts = ""
|
|
|
- } else if len(flag.NoOptDefVal) > 0 {
|
|
|
+ } else if flag.NoOptDefVal != "" {
|
|
|
+ // '-f' (arg was optional)
|
|
|
value = flag.NoOptDefVal
|
|
|
} else if len(shorthands) > 1 {
|
|
|
+ // '-farg'
|
|
|
value = shorthands[1:]
|
|
|
outShorts = ""
|
|
|
} else if len(args) > 0 {
|
|
|
+ // '-f arg'
|
|
|
value = args[0]
|
|
|
outArgs = args[1:]
|
|
|
} else {
|
|
|
+ // '-f' (arg was required)
|
|
|
err = f.failf("flag needs an argument: %q in -%s", c, shorthands)
|
|
|
return
|
|
|
}
|
|
|
- err = f.setFlag(flag, value, shorthands)
|
|
|
+
|
|
|
+ if flag.ShorthandDeprecated != "" {
|
|
|
+ fmt.Fprintf(f.out(), "Flag shorthand -%s has been deprecated, %s\n", flag.Shorthand, flag.ShorthandDeprecated)
|
|
|
+ }
|
|
|
+
|
|
|
+ err = fn(flag, value)
|
|
|
return
|
|
|
}
|
|
|
|
|
|
-func (f *FlagSet) parseShortArg(s string, args []string) (a []string, err error) {
|
|
|
+func (f *FlagSet) parseShortArg(s string, args []string, fn parseFunc) (a []string, err error) {
|
|
|
a = args
|
|
|
shorthands := s[1:]
|
|
|
|
|
|
+ // "shorthands" can be a series of shorthand letters of flags (e.g. "-vvv").
|
|
|
for len(shorthands) > 0 {
|
|
|
- shorthands, a, err = f.parseSingleShortArg(shorthands, args)
|
|
|
+ shorthands, a, err = f.parseSingleShortArg(shorthands, args, fn)
|
|
|
if err != nil {
|
|
|
return
|
|
|
}
|
|
|
@@ -732,7 +980,7 @@ func (f *FlagSet) parseShortArg(s string, args []string) (a []string, err error)
|
|
|
return
|
|
|
}
|
|
|
|
|
|
-func (f *FlagSet) parseArgs(args []string) (err error) {
|
|
|
+func (f *FlagSet) parseArgs(args []string, fn parseFunc) (err error) {
|
|
|
for len(args) > 0 {
|
|
|
s := args[0]
|
|
|
args = args[1:]
|
|
|
@@ -752,9 +1000,9 @@ func (f *FlagSet) parseArgs(args []string) (err error) {
|
|
|
f.args = append(f.args, args...)
|
|
|
break
|
|
|
}
|
|
|
- args, err = f.parseLongArg(s, args)
|
|
|
+ args, err = f.parseLongArg(s, args, fn)
|
|
|
} else {
|
|
|
- args, err = f.parseShortArg(s, args)
|
|
|
+ args, err = f.parseShortArg(s, args, fn)
|
|
|
}
|
|
|
if err != nil {
|
|
|
return
|
|
|
@@ -769,8 +1017,43 @@ func (f *FlagSet) parseArgs(args []string) (err error) {
|
|
|
// The return value will be ErrHelp if -help was set but not defined.
|
|
|
func (f *FlagSet) Parse(arguments []string) error {
|
|
|
f.parsed = true
|
|
|
+
|
|
|
+ if len(arguments) < 0 {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ f.args = make([]string, 0, len(arguments))
|
|
|
+
|
|
|
+ set := func(flag *Flag, value string) error {
|
|
|
+ return f.Set(flag.Name, value)
|
|
|
+ }
|
|
|
+
|
|
|
+ err := f.parseArgs(arguments, set)
|
|
|
+ if err != nil {
|
|
|
+ switch f.errorHandling {
|
|
|
+ case ContinueOnError:
|
|
|
+ return err
|
|
|
+ case ExitOnError:
|
|
|
+ os.Exit(2)
|
|
|
+ case PanicOnError:
|
|
|
+ panic(err)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+type parseFunc func(flag *Flag, value string) error
|
|
|
+
|
|
|
+// ParseAll parses flag definitions from the argument list, which should not
|
|
|
+// include the command name. The arguments for fn are flag and value. 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) ParseAll(arguments []string, fn func(flag *Flag, value string) error) error {
|
|
|
+ f.parsed = true
|
|
|
f.args = make([]string, 0, len(arguments))
|
|
|
- err := f.parseArgs(arguments)
|
|
|
+
|
|
|
+ err := f.parseArgs(arguments, fn)
|
|
|
if err != nil {
|
|
|
switch f.errorHandling {
|
|
|
case ContinueOnError:
|
|
|
@@ -796,6 +1079,14 @@ func Parse() {
|
|
|
CommandLine.Parse(os.Args[1:])
|
|
|
}
|
|
|
|
|
|
+// ParseAll parses the command-line flags from os.Args[1:] and called fn for each.
|
|
|
+// The arguments for fn are flag and value. Must be called after all flags are
|
|
|
+// defined and before flags are accessed by the program.
|
|
|
+func ParseAll(fn func(flag *Flag, value string) error) {
|
|
|
+ // Ignore errors; CommandLine is set for ExitOnError.
|
|
|
+ CommandLine.ParseAll(os.Args[1:], fn)
|
|
|
+}
|
|
|
+
|
|
|
// SetInterspersed sets whether to support interspersed option/non-option arguments.
|
|
|
func SetInterspersed(interspersed bool) {
|
|
|
CommandLine.SetInterspersed(interspersed)
|
|
|
@@ -806,17 +1097,18 @@ func Parsed() bool {
|
|
|
return CommandLine.Parsed()
|
|
|
}
|
|
|
|
|
|
-// The default set of command-line flags, parsed from os.Args.
|
|
|
+// CommandLine is 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.
|
|
|
+// NewFlagSet returns a new, empty flag set with the specified name,
|
|
|
+// error handling property and SortFlags set to true.
|
|
|
func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet {
|
|
|
f := &FlagSet{
|
|
|
name: name,
|
|
|
errorHandling: errorHandling,
|
|
|
argsLenAtDash: -1,
|
|
|
interspersed: true,
|
|
|
+ SortFlags: true,
|
|
|
}
|
|
|
return f
|
|
|
}
|