// Package getopt implements a command-line argument parser. // // It tries to cover all common styles of option syntaxes, and provides context // information when given a partial input. It is mainly useful for writing // completion engines and wrapper programs. // // If you are looking for an option parser for your go program, consider using // the flag package in the standard library instead.
package getopt //go:generate stringer -type=Config,HasArg,ContextType -output=string.go import // Getopt specifies the syntax of command-line arguments. type Getopt struct { Options []*Option Config Config } // Config configurates the parsing behavior. type Config uint const ( // DoubleDashTerminatesOptions indicates that all elements after an argument // "--" are treated as arguments. DoubleDashTerminatesOptions Config = 1 << iota // FirstArgTerminatesOptions indicates that all elements after the first // argument are treated as arguments. FirstArgTerminatesOptions // LongOnly indicates that long options may be started by either one or two // dashes, and short options are not allowed. Should replicate the behavior // of getopt_long_only and the // flag package of the Go standard library. LongOnly // GNUGetoptLong is a configuration that should replicate the behavior of // GNU getopt_long. GNUGetoptLong = DoubleDashTerminatesOptions // POSIXGetopt is a configuration that should replicate the behavior of // POSIX getopt. POSIXGetopt = DoubleDashTerminatesOptions | FirstArgTerminatesOptions ) // HasAll tests whether a configuration has all specified flags set. func ( Config) ( Config) bool { return ( & ) == } // Option is a command-line option. type Option struct { // Short option. Set to 0 for long-only. Short rune // Long option. Set to "" for short-only. Long string // Whether the option takes an argument, and whether it is required. HasArg HasArg } // HasArg indicates whether an option takes an argument, and whether it is // required. type HasArg uint const ( // NoArgument indicates that an option takes no argument. NoArgument HasArg = iota // RequiredArgument indicates that an option must take an argument. The // argument can come either directly after a short option (-oarg), after a // long option followed by an equal sign (--long=arg), or as a subsequent // argument after the option (-o arg, --long arg). RequiredArgument // OptionalArgument indicates that an option takes an optional argument. // The argument can come either directly after a short option (-oarg) or // after a long option followed by an equal sign (--long=arg). OptionalArgument ) // ParsedOption represents a parsed option. type ParsedOption struct { Option *Option Long bool Argument string } // Context indicates what may come after the supplied argument list. type Context struct { // The nature of the context. Type ContextType // Current option, with a likely incomplete Argument. Non-nil when Type is // OptionArgument. Option *ParsedOption // Current partial long option name or argument. Non-empty when Type is // LongOption or Argument. Text string } // ContextType encodes what may be appended to the last element of the argument // list. type ContextType uint const ( // NewOptionOrArgument indicates that the last element may be either a new // option or a new argument. Returned when it is an empty string. NewOptionOrArgument ContextType = iota // NewOption indicates that the last element must be new option, short or // long. Returned when it is "-". NewOption // NewLongOption indicates that the last element must be a new long option. // Returned when it is "--". NewLongOption // LongOption indicates that the last element is a long option, but not its // argument. The partial name of the long option is stored in Context.Text. LongOption // ChainShortOption indicates that a new short option may be chained. // Returned when the last element consists of a chain of options that take // no arguments. ChainShortOption // OptionArgument indicates that the last element list must be an argument // to an option. The option in question is stored in Context.Option. OptionArgument // Argument indicates that the last element is an argument. The partial // argument is stored in Context.Text. Argument ) func ( *Getopt) ( rune) *Option { for , := range .Options { if == .Short { return } } return nil } // parseShort parse short options, without the leading dash. It returns the // parsed options and whether an argument is still to be seen. func ( *Getopt) ( string) ([]*ParsedOption, bool) { var []*ParsedOption var bool for , := range { := .findShort() if != nil { if .HasArg == NoArgument { = append(, &ParsedOption{, false, ""}) continue } else { := &ParsedOption{, false, [+len(string()):]} = append(, ) = .Argument == "" && .HasArg == RequiredArgument break } } // Unknown option, treat as taking an optional argument := &ParsedOption{ &Option{, "", OptionalArgument}, false, [+len(string()):]} = append(, ) break } return , } // parseLong parse a long option, without the leading dashes. It returns the // parsed option and whether an argument is still to be seen. func ( *Getopt) ( string) (*ParsedOption, bool) { := strings.IndexRune(, '=') for , := range .Options { if == .Long { return &ParsedOption{, true, ""}, .HasArg == RequiredArgument } else if != -1 && [:] == .Long { return &ParsedOption{, true, [+1:]}, false } } // Unknown option, treat as taking an optional argument if == -1 { return &ParsedOption{&Option{0, , OptionalArgument}, true, ""}, false } return &ParsedOption{&Option{0, [:], OptionalArgument}, true, [+1:]}, false } // Parse parses an argument list. func ( *Getopt) ( []string) ([]*ParsedOption, []string, *Context) { var ( []*ParsedOption []string // Non-nil only when the last element was an option with required // argument, but the argument has not been seen. *ParsedOption // True if an option terminator has been seen. The criteria of option // terminators is determined by the configuration. bool ) var string := func( string) bool { return strings.HasPrefix(, ) } for _, = range [:len()-1] { if != nil { .Argument = = append(, ) = nil } else if { = append(, ) } else if .Config.HasAll(DoubleDashTerminatesOptions) && == "--" { = true } else if ("--") { , := .parseLong([2:]) if { = } else { = append(, ) } } else if ("-") { if .Config.HasAll(LongOnly) { , := .parseLong([1:]) if { = } else { = append(, ) } } else { , := .parseShort([1:]) if { = append(, [:len()-1]...) = [len()-1] } else { = append(, ...) } } } else { = append(, ) if .Config.HasAll(FirstArgTerminatesOptions) { = true } } } = [len()-1] := &Context{} if != nil { .Argument = .Type, .Option = OptionArgument, } else if { .Type, .Text = Argument, } else if == "" { .Type = NewOptionOrArgument } else if == "-" { .Type = NewOption } else if == "--" { .Type = NewLongOption } else if ("--") { if !strings.ContainsRune(, '=') { .Type, .Text = LongOption, [2:] } else { , := .parseLong([2:]) .Type, .Option = OptionArgument, } } else if ("-") { if .Config.HasAll(LongOnly) { if !strings.ContainsRune(, '=') { .Type, .Text = LongOption, [1:] } else { , := .parseLong([1:]) .Type, .Option = OptionArgument, } } else { , := .parseShort([1:]) if [len()-1].Option.HasArg == NoArgument { = append(, ...) .Type = ChainShortOption } else { = append(, [:len()-1]...) .Type, .Option = OptionArgument, [len()-1] } } } else { .Type, .Text = Argument, } return , , }