Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1227 lines
35 KiB
1227 lines
35 KiB
// 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. |
|
|
|
/* |
|
Package pflag is a drop-in replacement for Go's flag package, implementing |
|
POSIX/GNU-style --flags. |
|
|
|
pflag is compatible with the GNU extensions to the POSIX recommendations |
|
for command-line options. See |
|
http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html |
|
|
|
Usage: |
|
|
|
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/spf13/pflag" |
|
|
|
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 |
|
unaffected. |
|
|
|
Define flags using flag.String(), Bool(), Int(), etc. |
|
|
|
This declares an integer flag, -flagname, stored in the pointer ip, with type *int. |
|
var ip = flag.Int("flagname", 1234, "help message for flagname") |
|
If you like, you can bind the flag to a variable using the Var() functions. |
|
var flagvar int |
|
func init() { |
|
flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname") |
|
} |
|
Or you can create custom flags that satisfy the Value interface (with |
|
pointer receivers) and couple them to flag parsing by |
|
flag.Var(&flagVal, "name", "help message for flagname") |
|
For such flags, the default value is just the initial value of the variable. |
|
|
|
After all flags are defined, call |
|
flag.Parse() |
|
to parse the command line into the defined flags. |
|
|
|
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) |
|
|
|
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 through flag.NArg()-1. |
|
|
|
The pflag package also defines some new functions that are not in flag, |
|
that give one-letter shorthands for flags. You can use these by appending |
|
'P' to the name of any function that defines a flag. |
|
var ip = flag.IntP("flagname", "f", 1234, "help message") |
|
var flagvar bool |
|
func init() { |
|
flag.BoolVarP("boolname", "b", true, "help message") |
|
} |
|
flag.VarP(&flagVar, "varname", "v", 1234, "help message") |
|
Shorthand letters can be used with single dashes on the command line. |
|
Boolean shorthand flags can be combined with other shorthand flags. |
|
|
|
Command line flag syntax: |
|
--flag // boolean flags only |
|
--flag=x |
|
|
|
Unlike the flag package, a single dash before an option means something |
|
different than a double dash. Single dashes signify a series of shorthand |
|
letters for flags. All but the last shorthand letter must be boolean flags. |
|
// boolean flags |
|
-f |
|
-abc |
|
// non-boolean flags |
|
-n 1234 |
|
-Ifile |
|
// mixed |
|
-abcs "hello" |
|
-abcn1234 |
|
|
|
Flag parsing stops after the terminator "--". Unlike the flag package, |
|
flags can be interspersed with arguments anywhere on the command line |
|
before this terminator. |
|
|
|
Integer flags accept 1234, 0664, 0x1234 and may be negative. |
|
Boolean flags (in their long form) accept 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 |
|
independent sets of flags, such as to implement subcommands |
|
in a command-line interface. The methods of FlagSet are |
|
analogous to the top-level functions for the command-line |
|
flag set. |
|
*/ |
|
package pflag |
|
|
|
import ( |
|
"bytes" |
|
"errors" |
|
goflag "flag" |
|
"fmt" |
|
"io" |
|
"os" |
|
"sort" |
|
"strings" |
|
) |
|
|
|
// ErrHelp is the error returned if the flag -help is invoked but no such flag is defined. |
|
var ErrHelp = errors.New("pflag: help requested") |
|
|
|
// ErrorHandling defines how to handle flag parsing errors. |
|
type ErrorHandling int |
|
|
|
const ( |
|
// ContinueOnError will return an err from Parse() if an error is found |
|
ContinueOnError ErrorHandling = iota |
|
// ExitOnError will call os.Exit(2) if an error is found when parsing |
|
ExitOnError |
|
// PanicOnError will panic() if an error is found when parsing flags |
|
PanicOnError |
|
) |
|
|
|
// ParseErrorsWhitelist defines the parsing errors that can be ignored |
|
type ParseErrorsWhitelist struct { |
|
// UnknownFlags will ignore unknown flags errors and continue parsing rest of the flags |
|
UnknownFlags bool |
|
} |
|
|
|
// NormalizedName is a flag name that has been normalized according to rules |
|
// for the FlagSet (e.g. making '-' and '_' equivalent). |
|
type NormalizedName string |
|
|
|
// 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() |
|
|
|
// SortFlags is used to indicate, if user wants to have sorted flags in |
|
// help/usage messages. |
|
SortFlags bool |
|
|
|
// ParseErrorsWhitelist is used to configure a whitelist of errors |
|
ParseErrorsWhitelist ParseErrorsWhitelist |
|
|
|
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 -- |
|
errorHandling ErrorHandling |
|
output io.Writer // nil means stderr; use out() accessor |
|
interspersed bool // allow interspersed option/non-option args |
|
normalizeNameFunc func(f *FlagSet, name string) NormalizedName |
|
|
|
addedGoFlagSets []*goflag.FlagSet |
|
} |
|
|
|
// A Flag represents the state of a flag. |
|
type Flag struct { |
|
Name string // name as it appears on command line |
|
Shorthand string // one-letter abbreviated flag |
|
Usage string // help message |
|
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 |
|
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 |
|
Annotations map[string][]string // used by cobra.Command bash autocomple code |
|
} |
|
|
|
// 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) error |
|
Type() string |
|
} |
|
|
|
// sortFlags returns the flags as a slice in lexicographical sorted order. |
|
func sortFlags(flags map[NormalizedName]*Flag) []*Flag { |
|
list := make(sort.StringSlice, len(flags)) |
|
i := 0 |
|
for k := range flags { |
|
list[i] = string(k) |
|
i++ |
|
} |
|
list.Sort() |
|
result := make([]*Flag, len(list)) |
|
for i, name := range list { |
|
result[i] = flags[NormalizedName(name)] |
|
} |
|
return result |
|
} |
|
|
|
// SetNormalizeFunc allows you to add a function which can translate flag names. |
|
// Flags added to the FlagSet will be translated and then when anything tries to |
|
// look up the flag that will also be translated. So it would be possible to create |
|
// a flag named "getURL" and have it translated to "geturl". A user could then pass |
|
// "--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 |
|
f.sortedFormal = f.sortedFormal[:0] |
|
for fname, flag := range f.formal { |
|
nname := f.normalizeFlagName(flag.Name) |
|
if fname == nname { |
|
continue |
|
} |
|
flag.Name = string(nname) |
|
delete(f.formal, fname) |
|
f.formal[nname] = flag |
|
if _, set := f.actual[fname]; set { |
|
delete(f.actual, fname) |
|
f.actual[nname] = flag |
|
} |
|
} |
|
} |
|
|
|
// GetNormalizeFunc returns the previously set NormalizeFunc of a function which |
|
// does no translation, if not set previously. |
|
func (f *FlagSet) GetNormalizeFunc() func(f *FlagSet, name string) NormalizedName { |
|
if f.normalizeNameFunc != nil { |
|
return f.normalizeNameFunc |
|
} |
|
return func(f *FlagSet, name string) NormalizedName { return NormalizedName(name) } |
|
} |
|
|
|
func (f *FlagSet) normalizeFlagName(name string) NormalizedName { |
|
n := f.GetNormalizeFunc() |
|
return n(f, name) |
|
} |
|
|
|
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 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)) { |
|
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) |
|
} |
|
} |
|
|
|
// HasFlags returns a bool to indicate if the FlagSet has any flags defined. |
|
func (f *FlagSet) HasFlags() bool { |
|
return len(f.formal) > 0 |
|
} |
|
|
|
// HasAvailableFlags returns a bool to indicate if the FlagSet has any flags |
|
// that are not hidden. |
|
func (f *FlagSet) HasAvailableFlags() bool { |
|
for _, flag := range f.formal { |
|
if !flag.Hidden { |
|
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 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)) { |
|
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 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) |
|
} |
|
|
|
// Lookup returns the Flag structure of the named flag, returning nil if none exists. |
|
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] |
|
} |
|
|
|
// func to return a given type for a given flag name |
|
func (f *FlagSet) getFlagType(name string, ftype string, convFunc func(sval string) (interface{}, error)) (interface{}, error) { |
|
flag := f.Lookup(name) |
|
if flag == nil { |
|
err := fmt.Errorf("flag accessed but not defined: %s", name) |
|
return nil, err |
|
} |
|
|
|
if flag.Value.Type() != ftype { |
|
err := fmt.Errorf("trying to get %s value of flag of type %s", ftype, flag.Value.Type()) |
|
return nil, err |
|
} |
|
|
|
sval := flag.Value.String() |
|
result, err := convFunc(sval) |
|
if err != nil { |
|
return nil, err |
|
} |
|
return result, nil |
|
} |
|
|
|
// ArgsLenAtDash will return the length of f.Args at the moment when a -- was |
|
// found during arg parsing. This allows your program to know which args were |
|
// before the -- and which came after. |
|
func (f *FlagSet) ArgsLenAtDash() int { |
|
return f.argsLenAtDash |
|
} |
|
|
|
// MarkDeprecated indicated that a flag is deprecated in your program. It will |
|
// continue to function but will not show up in help or usage messages. Using |
|
// this flag will also print the given usageMessage. |
|
func (f *FlagSet) MarkDeprecated(name string, usageMessage string) error { |
|
flag := f.Lookup(name) |
|
if flag == nil { |
|
return fmt.Errorf("flag %q does not exist", name) |
|
} |
|
if usageMessage == "" { |
|
return fmt.Errorf("deprecated message for flag %q must be set", name) |
|
} |
|
flag.Deprecated = usageMessage |
|
flag.Hidden = true |
|
return nil |
|
} |
|
|
|
// MarkShorthandDeprecated will mark the shorthand of a flag deprecated in your |
|
// program. It will continue to function but will not show up in help or usage |
|
// messages. Using this flag will also print the given usageMessage. |
|
func (f *FlagSet) MarkShorthandDeprecated(name string, usageMessage string) error { |
|
flag := f.Lookup(name) |
|
if flag == nil { |
|
return fmt.Errorf("flag %q does not exist", name) |
|
} |
|
if usageMessage == "" { |
|
return fmt.Errorf("deprecated message for flag %q must be set", name) |
|
} |
|
flag.ShorthandDeprecated = usageMessage |
|
return nil |
|
} |
|
|
|
// MarkHidden sets a flag to 'hidden' in your program. It will continue to |
|
// function but will not show up in help or usage messages. |
|
func (f *FlagSet) MarkHidden(name string) error { |
|
flag := f.Lookup(name) |
|
if flag == nil { |
|
return fmt.Errorf("flag %q does not exist", name) |
|
} |
|
flag.Hidden = true |
|
return nil |
|
} |
|
|
|
// Lookup returns the Flag structure of the named command-line flag, |
|
// returning nil if none exists. |
|
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) |
|
flag, ok := f.formal[normalName] |
|
if !ok { |
|
return fmt.Errorf("no such flag -%v", name) |
|
} |
|
|
|
err := flag.Value.Set(value) |
|
if err != nil { |
|
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 !flag.Changed { |
|
if f.actual == nil { |
|
f.actual = make(map[NormalizedName]*Flag) |
|
} |
|
f.actual[normalName] = flag |
|
f.orderedActual = append(f.orderedActual, flag) |
|
|
|
flag.Changed = true |
|
} |
|
|
|
if flag.Deprecated != "" { |
|
fmt.Fprintf(f.out(), "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated) |
|
} |
|
return nil |
|
} |
|
|
|
// SetAnnotation allows one to set arbitrary annotations on a flag in the FlagSet. |
|
// This is sometimes used by spf13/cobra programs which want to generate additional |
|
// bash completion information. |
|
func (f *FlagSet) SetAnnotation(name, key string, values []string) error { |
|
normalName := f.normalizeFlagName(name) |
|
flag, ok := f.formal[normalName] |
|
if !ok { |
|
return fmt.Errorf("no such flag -%v", name) |
|
} |
|
if flag.Annotations == nil { |
|
flag.Annotations = map[string][]string{} |
|
} |
|
flag.Annotations[key] = values |
|
return nil |
|
} |
|
|
|
// Changed returns true if the flag was explicitly set during Parse() and false |
|
// otherwise |
|
func (f *FlagSet) Changed(name string) bool { |
|
flag := f.Lookup(name) |
|
// If a flag doesn't exist, it wasn't changed.... |
|
if flag == nil { |
|
return false |
|
} |
|
return flag.Changed |
|
} |
|
|
|
// 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 unless configured |
|
// otherwise, the default values of all defined flags in the set. |
|
func (f *FlagSet) PrintDefaults() { |
|
usages := f.FlagUsages() |
|
fmt.Fprint(f.out(), usages) |
|
} |
|
|
|
// 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" |
|
case "stringSlice": |
|
name = "strings" |
|
case "intSlice": |
|
name = "ints" |
|
case "uintSlice": |
|
name = "uints" |
|
case "boolSlice": |
|
name = "bools" |
|
} |
|
|
|
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\n") |
|
if w <= 0 { |
|
return s, "" |
|
} |
|
nlPos := strings.LastIndex(s[:i], "\n") |
|
if nlPos > 0 && nlPos < w { |
|
return s[:nlPos], s[nlPos+1:] |
|
} |
|
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 strings.Replace(s, "\n", "\n"+strings.Repeat(" ", i), -1) |
|
} |
|
|
|
// 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 strings.Replace(s, "\n", r, -1) |
|
} |
|
|
|
// 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 + strings.Replace(l, "\n", "\n"+strings.Repeat(" ", i), -1) |
|
|
|
// Now wrap the rest |
|
for s != "" { |
|
var t string |
|
|
|
t, s = wrapN(wrap, slop, s) |
|
r = r + "\n" + strings.Repeat(" ", i) + strings.Replace(t, "\n", "\n"+strings.Repeat(" ", i), -1) |
|
} |
|
|
|
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 flag.Hidden { |
|
return |
|
} |
|
|
|
line := "" |
|
if flag.Shorthand != "" && flag.ShorthandDeprecated == "" { |
|
line = fmt.Sprintf(" -%s, --%s", flag.Shorthand, flag.Name) |
|
} else { |
|
line = fmt.Sprintf(" --%s", flag.Name) |
|
} |
|
|
|
varname, usage := UnquoteUsage(flag) |
|
if varname != "" { |
|
line += " " + varname |
|
} |
|
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) |
|
} |
|
case "count": |
|
if flag.NoOptDefVal != "+1" { |
|
line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) |
|
} |
|
default: |
|
line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) |
|
} |
|
} |
|
|
|
// This special character will be replaced with spacing once the |
|
// correct alignment is calculated |
|
line += "\x00" |
|
if len(line) > maxlen { |
|
maxlen = len(line) |
|
} |
|
|
|
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) |
|
} |
|
} |
|
if len(flag.Deprecated) != 0 { |
|
line += fmt.Sprintf(" (DEPRECATED: %s)", flag.Deprecated) |
|
} |
|
|
|
lines = append(lines, line) |
|
}) |
|
|
|
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. |
|
func PrintDefaults() { |
|
CommandLine.PrintDefaults() |
|
} |
|
|
|
// defaultUsage is the default function to print a usage message. |
|
func defaultUsage(f *FlagSet) { |
|
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. |
|
// 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() |
|
} |
|
|
|
// NFlag returns the number of flags that have been set. |
|
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 |
|
// after flags have been processed. |
|
func Arg(i int) string { |
|
return CommandLine.Arg(i) |
|
} |
|
|
|
// NArg is the number of arguments remaining after flags have been processed. |
|
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. |
|
func Args() []string { return CommandLine.args } |
|
|
|
// 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) { |
|
f.VarP(value, name, "", usage) |
|
} |
|
|
|
// VarPF is like VarP, but returns the flag created |
|
func (f *FlagSet) VarPF(value Value, name, shorthand, usage string) *Flag { |
|
// Remember the default value as a string; it won't change. |
|
flag := &Flag{ |
|
Name: name, |
|
Shorthand: shorthand, |
|
Usage: usage, |
|
Value: value, |
|
DefValue: value.String(), |
|
} |
|
f.AddFlag(flag) |
|
return 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) |
|
} |
|
|
|
// AddFlag will add the flag to the FlagSet |
|
func (f *FlagSet) AddFlag(flag *Flag) { |
|
normalizedFlagName := f.normalizeFlagName(flag.Name) |
|
|
|
_, 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 |
|
} |
|
if f.formal == nil { |
|
f.formal = make(map[NormalizedName]*Flag) |
|
} |
|
|
|
flag.Name = string(normalizedFlagName) |
|
f.formal[normalizedFlagName] = flag |
|
f.orderedFormal = append(f.orderedFormal, flag) |
|
|
|
if flag.Shorthand == "" { |
|
return |
|
} |
|
if len(flag.Shorthand) > 1 { |
|
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] |
|
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. |
|
func (f *FlagSet) AddFlagSet(newSet *FlagSet) { |
|
if newSet == nil { |
|
return |
|
} |
|
newSet.VisitAll(func(flag *Flag) { |
|
if f.Lookup(flag.Name) == nil { |
|
f.AddFlag(flag) |
|
} |
|
}) |
|
} |
|
|
|
// 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 Var(value Value, name string, usage string) { |
|
CommandLine.VarP(value, name, "", usage) |
|
} |
|
|
|
// VarP is like Var, but accepts a shorthand letter that can be used after a single dash. |
|
func VarP(value Value, name, shorthand, usage string) { |
|
CommandLine.VarP(value, name, shorthand, usage) |
|
} |
|
|
|
// failf prints to standard error a formatted error and usage message and |
|
// returns the error. |
|
func (f *FlagSet) failf(format string, a ...interface{}) error { |
|
err := fmt.Errorf(format, a...) |
|
if f.errorHandling != ContinueOnError { |
|
fmt.Fprintln(f.out(), err) |
|
f.usage() |
|
} |
|
return err |
|
} |
|
|
|
// usage calls the Usage method for the flag set, or the usage function if |
|
// the flag set is CommandLine. |
|
func (f *FlagSet) usage() { |
|
if f == CommandLine { |
|
Usage() |
|
} else if f.Usage == nil { |
|
defaultUsage(f) |
|
} else { |
|
f.Usage() |
|
} |
|
} |
|
|
|
//--unknown (args will be empty) |
|
//--unknown --next-flag ... (args will be --next-flag ...) |
|
//--unknown arg ... (args will be arg ...) |
|
func stripUnknownFlagValue(args []string) []string { |
|
if len(args) == 0 { |
|
//--unknown |
|
return args |
|
} |
|
|
|
first := args[0] |
|
if len(first) > 0 && first[0] == '-' { |
|
//--unknown --next-flag ... |
|
return args |
|
} |
|
|
|
//--unknown arg ... (args will be arg ...) |
|
if len(args) > 1 { |
|
return args[1:] |
|
} |
|
return nil |
|
} |
|
|
|
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, exists := f.formal[f.normalizeFlagName(name)] |
|
|
|
if !exists { |
|
switch { |
|
case name == "help": |
|
f.usage() |
|
return a, ErrHelp |
|
case f.ParseErrorsWhitelist.UnknownFlags: |
|
// --unknown=unknownval arg ... |
|
// we do not want to lose arg in this case |
|
if len(split) >= 2 { |
|
return a, nil |
|
} |
|
|
|
return stripUnknownFlagValue(a), nil |
|
default: |
|
err = f.failf("unknown flag: --%s", name) |
|
return |
|
} |
|
} |
|
|
|
var value string |
|
if len(split) == 2 { |
|
// '--flag=arg' |
|
value = split[1] |
|
} else if flag.NoOptDefVal != "" { |
|
// '--flag' (arg was optional) |
|
value = flag.NoOptDefVal |
|
} else if len(a) > 0 { |
|
// '--flag arg' |
|
value = a[0] |
|
a = a[1:] |
|
} else { |
|
// '--flag' (arg was required) |
|
err = f.failf("flag needs an argument: %s", s) |
|
return |
|
} |
|
|
|
err = fn(flag, value) |
|
if err != nil { |
|
f.failf(err.Error()) |
|
} |
|
return |
|
} |
|
|
|
func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parseFunc) (outShorts string, outArgs []string, err error) { |
|
outArgs = args |
|
|
|
if strings.HasPrefix(shorthands, "test.") { |
|
return |
|
} |
|
|
|
outShorts = shorthands[1:] |
|
c := shorthands[0] |
|
|
|
flag, exists := f.shorthands[c] |
|
if !exists { |
|
switch { |
|
case c == 'h': |
|
f.usage() |
|
err = ErrHelp |
|
return |
|
case f.ParseErrorsWhitelist.UnknownFlags: |
|
// '-f=arg arg ...' |
|
// we do not want to lose arg in this case |
|
if len(shorthands) > 2 && shorthands[1] == '=' { |
|
outShorts = "" |
|
return |
|
} |
|
|
|
outArgs = stripUnknownFlagValue(outArgs) |
|
return |
|
default: |
|
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 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 |
|
} |
|
|
|
if flag.ShorthandDeprecated != "" { |
|
fmt.Fprintf(f.out(), "Flag shorthand -%s has been deprecated, %s\n", flag.Shorthand, flag.ShorthandDeprecated) |
|
} |
|
|
|
err = fn(flag, value) |
|
if err != nil { |
|
f.failf(err.Error()) |
|
} |
|
return |
|
} |
|
|
|
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, fn) |
|
if err != nil { |
|
return |
|
} |
|
} |
|
|
|
return |
|
} |
|
|
|
func (f *FlagSet) parseArgs(args []string, fn parseFunc) (err error) { |
|
for len(args) > 0 { |
|
s := args[0] |
|
args = args[1:] |
|
if len(s) == 0 || s[0] != '-' || len(s) == 1 { |
|
if !f.interspersed { |
|
f.args = append(f.args, s) |
|
f.args = append(f.args, args...) |
|
return nil |
|
} |
|
f.args = append(f.args, s) |
|
continue |
|
} |
|
|
|
if s[1] == '-' { |
|
if len(s) == 2 { // "--" terminates the flags |
|
f.argsLenAtDash = len(f.args) |
|
f.args = append(f.args, args...) |
|
break |
|
} |
|
args, err = f.parseLongArg(s, args, fn) |
|
} else { |
|
args, err = f.parseShortArg(s, args, fn) |
|
} |
|
if err != nil { |
|
return |
|
} |
|
} |
|
return |
|
} |
|
|
|
// Parse parses flag definitions from the argument list, which should not |
|
// 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) error { |
|
if f.addedGoFlagSets != nil { |
|
for _, goFlagSet := range f.addedGoFlagSets { |
|
goFlagSet.Parse(nil) |
|
} |
|
} |
|
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: |
|
fmt.Println(err) |
|
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, fn) |
|
if err != nil { |
|
switch f.errorHandling { |
|
case ContinueOnError: |
|
return err |
|
case ExitOnError: |
|
os.Exit(2) |
|
case PanicOnError: |
|
panic(err) |
|
} |
|
} |
|
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() { |
|
// Ignore errors; CommandLine is set for ExitOnError. |
|
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) |
|
} |
|
|
|
// Parsed returns true if the command-line flags have been parsed. |
|
func Parsed() bool { |
|
return CommandLine.Parsed() |
|
} |
|
|
|
// 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, |
|
// 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 |
|
} |
|
|
|
// SetInterspersed sets whether to support interspersed option/non-option arguments. |
|
func (f *FlagSet) SetInterspersed(interspersed bool) { |
|
f.interspersed = interspersed |
|
} |
|
|
|
// 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 |
|
f.argsLenAtDash = -1 |
|
}
|
|
|