| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- package flags
- import (
- "bytes"
- "fmt"
- "os"
- "strings"
- "unicode/utf8"
- )
- type parseState struct {
- arg string
- args []string
- retargs []string
- err error
- command *Command
- lookup lookup
- }
- func (p *parseState) eof() bool {
- return len(p.args) == 0
- }
- func (p *parseState) pop() string {
- if p.eof() {
- return ""
- }
- p.arg = p.args[0]
- p.args = p.args[1:]
- return p.arg
- }
- func (p *parseState) peek() string {
- if p.eof() {
- return ""
- }
- return p.args[0]
- }
- func (p *parseState) checkRequired() error {
- required := p.lookup.required
- if len(required) == 0 {
- return nil
- }
- names := make([]string, 0, len(required))
- for k := range required {
- names = append(names, "`"+k.String()+"'")
- }
- var msg string
- if len(names) == 1 {
- msg = fmt.Sprintf("the required flag %s was not specified", names[0])
- } else {
- msg = fmt.Sprintf("the required flags %s and %s were not specified",
- strings.Join(names[:len(names)-1], ", "), names[len(names)-1])
- }
- p.err = newError(ErrRequired, msg)
- return p.err
- }
- func (p *parseState) estimateCommand() error {
- commands := p.command.sortedCommands()
- cmdnames := make([]string, len(commands))
- for i, v := range commands {
- cmdnames[i] = v.Name
- }
- var msg string
- if len(p.retargs) != 0 {
- c, l := closestChoice(p.retargs[0], cmdnames)
- msg = fmt.Sprintf("Unknown command `%s'", p.retargs[0])
- if float32(l)/float32(len(c)) < 0.5 {
- msg = fmt.Sprintf("%s, did you mean `%s'?", msg, c)
- } else if len(cmdnames) == 1 {
- msg = fmt.Sprintf("%s. You should use the %s command",
- msg,
- cmdnames[0])
- } else {
- msg = fmt.Sprintf("%s. Please specify one command of: %s or %s",
- msg,
- strings.Join(cmdnames[:len(cmdnames)-1], ", "),
- cmdnames[len(cmdnames)-1])
- }
- } else {
- if len(cmdnames) == 1 {
- msg = fmt.Sprintf("Please specify the %s command", cmdnames[0])
- } else {
- msg = fmt.Sprintf("Please specify one command of: %s or %s",
- strings.Join(cmdnames[:len(cmdnames)-1], ", "),
- cmdnames[len(cmdnames)-1])
- }
- }
- return newError(ErrRequired, msg)
- }
- func (p *Parser) parseOption(s *parseState, name string, option *Option, canarg bool, argument *string) (retoption *Option, err error) {
- if !option.canArgument() {
- if argument != nil {
- msg := fmt.Sprintf("bool flag `%s' cannot have an argument", option)
- return option, newError(ErrNoArgumentForBool, msg)
- }
- err = option.set(nil)
- } else if argument != nil {
- err = option.set(argument)
- } else if canarg && !s.eof() {
- arg := s.pop()
- err = option.set(&arg)
- } else if option.OptionalArgument {
- option.clear()
- for _, v := range option.OptionalValue {
- err = option.set(&v)
- if err != nil {
- break
- }
- }
- } else {
- msg := fmt.Sprintf("expected argument for flag `%s'", option)
- err = newError(ErrExpectedArgument, msg)
- }
- if err != nil {
- if _, ok := err.(*Error); !ok {
- msg := fmt.Sprintf("invalid argument for flag `%s' (expected %s): %s",
- option,
- option.value.Type(),
- err.Error())
- err = newError(ErrMarshal, msg)
- }
- }
- return option, err
- }
- func (p *Parser) parseLong(s *parseState, name string, argument *string) (option *Option, err error) {
- if option := s.lookup.longNames[name]; option != nil {
- // Only long options that are required can consume an argument
- // from the argument list
- canarg := !option.OptionalArgument
- return p.parseOption(s, name, option, canarg, argument)
- }
- return nil, newError(ErrUnknownFlag, fmt.Sprintf("unknown flag `%s'", name))
- }
- func (p *Parser) splitShortConcatArg(s *parseState, optname string) (string, *string) {
- c, n := utf8.DecodeRuneInString(optname)
- if n == len(optname) {
- return optname, nil
- }
- first := string(c)
- if option := s.lookup.shortNames[first]; option != nil && option.canArgument() {
- arg := optname[n:]
- return first, &arg
- }
- return optname, nil
- }
- func (p *Parser) parseShort(s *parseState, optname string, argument *string) (option *Option, err error) {
- if argument == nil {
- optname, argument = p.splitShortConcatArg(s, optname)
- }
- for i, c := range optname {
- shortname := string(c)
- if option = s.lookup.shortNames[shortname]; option != nil {
- // Only the last short argument can consume an argument from
- // the arguments list, and only if it's non optional
- canarg := (i+utf8.RuneLen(c) == len(optname)) && !option.OptionalArgument
- if _, err := p.parseOption(s, shortname, option, canarg, argument); err != nil {
- return option, err
- }
- } else {
- return nil, newError(ErrUnknownFlag, fmt.Sprintf("unknown flag `%s'", shortname))
- }
- // Only the first option can have a concatted argument, so just
- // clear argument here
- argument = nil
- }
- return option, nil
- }
- func (p *Parser) parseNonOption(s *parseState) error {
- if cmd := s.lookup.commands[s.arg]; cmd != nil {
- if err := s.checkRequired(); err != nil {
- return err
- }
- s.command.Active = cmd
- s.command = cmd
- s.lookup = cmd.makeLookup()
- } else if (p.Options & PassAfterNonOption) != None {
- // If PassAfterNonOption is set then all remaining arguments
- // are considered positional
- s.retargs = append(append(s.retargs, s.arg), s.args...)
- s.args = []string{}
- } else {
- s.retargs = append(s.retargs, s.arg)
- }
- return nil
- }
- func (p *Parser) showBuiltinHelp() error {
- var b bytes.Buffer
- p.WriteHelp(&b)
- return newError(ErrHelp, b.String())
- }
- func (p *Parser) printError(err error) error {
- if err != nil && (p.Options&PrintErrors) != None {
- fmt.Fprintln(os.Stderr, err)
- }
- return err
- }
|