1// Copyright 2012 Jesse van den Kieboom. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package flags 6 7import ( 8 "bytes" 9 "fmt" 10 "os" 11 "path" 12 "reflect" 13 "sort" 14 "strings" 15 "unicode/utf8" 16) 17 18// A Parser provides command line option parsing. It can contain several 19// option groups each with their own set of options. 20type Parser struct { 21 // Embedded, see Command for more information 22 *Command 23 24 // A usage string to be displayed in the help message. 25 Usage string 26 27 // Option flags changing the behavior of the parser. 28 Options Options 29 30 // NamespaceDelimiter separates group namespaces and option long names 31 NamespaceDelimiter string 32 33 // EnvNamespaceDelimiter separates group env namespaces and env keys 34 EnvNamespaceDelimiter string 35 36 // UnknownOptionsHandler is a function which gets called when the parser 37 // encounters an unknown option. The function receives the unknown option 38 // name, a SplitArgument which specifies its value if set with an argument 39 // separator, and the remaining command line arguments. 40 // It should return a new list of remaining arguments to continue parsing, 41 // or an error to indicate a parse failure. 42 UnknownOptionHandler func(option string, arg SplitArgument, args []string) ([]string, error) 43 44 // CompletionHandler is a function gets called to handle the completion of 45 // items. By default, the items are printed and the application is exited. 46 // You can override this default behavior by specifying a custom CompletionHandler. 47 CompletionHandler func(items []Completion) 48 49 // CommandHandler is a function that gets called to handle execution of a 50 // command. By default, the command will simply be executed. This can be 51 // overridden to perform certain actions (such as applying global flags) 52 // just before the command is executed. Note that if you override the 53 // handler it is your responsibility to call the command.Execute function. 54 // 55 // The command passed into CommandHandler may be nil in case there is no 56 // command to be executed when parsing has finished. 57 CommandHandler func(command Commander, args []string) error 58 59 internalError error 60} 61 62// SplitArgument represents the argument value of an option that was passed using 63// an argument separator. 64type SplitArgument interface { 65 // String returns the option's value as a string, and a boolean indicating 66 // if the option was present. 67 Value() (string, bool) 68} 69 70type strArgument struct { 71 value *string 72} 73 74func (s strArgument) Value() (string, bool) { 75 if s.value == nil { 76 return "", false 77 } 78 79 return *s.value, true 80} 81 82// Options provides parser options that change the behavior of the option 83// parser. 84type Options uint 85 86const ( 87 // None indicates no options. 88 None Options = 0 89 90 // HelpFlag adds a default Help Options group to the parser containing 91 // -h and --help options. When either -h or --help is specified on the 92 // command line, the parser will return the special error of type 93 // ErrHelp. When PrintErrors is also specified, then the help message 94 // will also be automatically printed to os.Stdout. 95 HelpFlag = 1 << iota 96 97 // PassDoubleDash passes all arguments after a double dash, --, as 98 // remaining command line arguments (i.e. they will not be parsed for 99 // flags). 100 PassDoubleDash 101 102 // IgnoreUnknown ignores any unknown options and passes them as 103 // remaining command line arguments instead of generating an error. 104 IgnoreUnknown 105 106 // PrintErrors prints any errors which occurred during parsing to 107 // os.Stderr. In the special case of ErrHelp, the message will be printed 108 // to os.Stdout. 109 PrintErrors 110 111 // PassAfterNonOption passes all arguments after the first non option 112 // as remaining command line arguments. This is equivalent to strict 113 // POSIX processing. 114 PassAfterNonOption 115 116 // Default is a convenient default set of options which should cover 117 // most of the uses of the flags package. 118 Default = HelpFlag | PrintErrors | PassDoubleDash 119) 120 121type parseState struct { 122 arg string 123 args []string 124 retargs []string 125 positional []*Arg 126 err error 127 128 command *Command 129 lookup lookup 130} 131 132// Parse is a convenience function to parse command line options with default 133// settings. The provided data is a pointer to a struct representing the 134// default option group (named "Application Options"). For more control, use 135// flags.NewParser. 136func Parse(data interface{}) ([]string, error) { 137 return NewParser(data, Default).Parse() 138} 139 140// ParseArgs is a convenience function to parse command line options with default 141// settings. The provided data is a pointer to a struct representing the 142// default option group (named "Application Options"). The args argument is 143// the list of command line arguments to parse. If you just want to parse the 144// default program command line arguments (i.e. os.Args), then use flags.Parse 145// instead. For more control, use flags.NewParser. 146func ParseArgs(data interface{}, args []string) ([]string, error) { 147 return NewParser(data, Default).ParseArgs(args) 148} 149 150// NewParser creates a new parser. It uses os.Args[0] as the application 151// name and then calls Parser.NewNamedParser (see Parser.NewNamedParser for 152// more details). The provided data is a pointer to a struct representing the 153// default option group (named "Application Options"), or nil if the default 154// group should not be added. The options parameter specifies a set of options 155// for the parser. 156func NewParser(data interface{}, options Options) *Parser { 157 p := NewNamedParser(path.Base(os.Args[0]), options) 158 159 if data != nil { 160 g, err := p.AddGroup("Application Options", "", data) 161 162 if err == nil { 163 g.parent = p 164 } 165 166 p.internalError = err 167 } 168 169 return p 170} 171 172// NewNamedParser creates a new parser. The appname is used to display the 173// executable name in the built-in help message. Option groups and commands can 174// be added to this parser by using AddGroup and AddCommand. 175func NewNamedParser(appname string, options Options) *Parser { 176 p := &Parser{ 177 Command: newCommand(appname, "", "", nil), 178 Options: options, 179 NamespaceDelimiter: ".", 180 EnvNamespaceDelimiter: "_", 181 } 182 183 p.Command.parent = p 184 185 return p 186} 187 188// Parse parses the command line arguments from os.Args using Parser.ParseArgs. 189// For more detailed information see ParseArgs. 190func (p *Parser) Parse() ([]string, error) { 191 return p.ParseArgs(os.Args[1:]) 192} 193 194// ParseArgs parses the command line arguments according to the option groups that 195// were added to the parser. On successful parsing of the arguments, the 196// remaining, non-option, arguments (if any) are returned. The returned error 197// indicates a parsing error and can be used with PrintError to display 198// contextual information on where the error occurred exactly. 199// 200// When the common help group has been added (AddHelp) and either -h or --help 201// was specified in the command line arguments, a help message will be 202// automatically printed if the PrintErrors option is enabled. 203// Furthermore, the special error type ErrHelp is returned. 204// It is up to the caller to exit the program if so desired. 205func (p *Parser) ParseArgs(args []string) ([]string, error) { 206 if p.internalError != nil { 207 return nil, p.internalError 208 } 209 210 p.eachOption(func(c *Command, g *Group, option *Option) { 211 option.clearReferenceBeforeSet = true 212 option.updateDefaultLiteral() 213 }) 214 215 // Add built-in help group to all commands if necessary 216 if (p.Options & HelpFlag) != None { 217 p.addHelpGroups(p.showBuiltinHelp) 218 } 219 220 compval := os.Getenv("GO_FLAGS_COMPLETION") 221 222 if len(compval) != 0 { 223 comp := &completion{parser: p} 224 items := comp.complete(args) 225 226 if p.CompletionHandler != nil { 227 p.CompletionHandler(items) 228 } else { 229 comp.print(items, compval == "verbose") 230 os.Exit(0) 231 } 232 233 return nil, nil 234 } 235 236 s := &parseState{ 237 args: args, 238 retargs: make([]string, 0, len(args)), 239 } 240 241 p.fillParseState(s) 242 243 for !s.eof() { 244 var err error 245 arg := s.pop() 246 247 // When PassDoubleDash is set and we encounter a --, then 248 // simply append all the rest as arguments and break out 249 if (p.Options&PassDoubleDash) != None && arg == "--" { 250 s.addArgs(s.args...) 251 break 252 } 253 254 if !argumentIsOption(arg) { 255 if (p.Options&PassAfterNonOption) != None && s.lookup.commands[arg] == nil { 256 // If PassAfterNonOption is set then all remaining arguments 257 // are considered positional 258 if err = s.addArgs(s.arg); err != nil { 259 break 260 } 261 262 if err = s.addArgs(s.args...); err != nil { 263 break 264 } 265 266 break 267 } 268 269 // Note: this also sets s.err, so we can just check for 270 // nil here and use s.err later 271 if p.parseNonOption(s) != nil { 272 break 273 } 274 275 continue 276 } 277 278 prefix, optname, islong := stripOptionPrefix(arg) 279 optname, _, argument := splitOption(prefix, optname, islong) 280 281 if islong { 282 err = p.parseLong(s, optname, argument) 283 } else { 284 err = p.parseShort(s, optname, argument) 285 } 286 287 if err != nil { 288 ignoreUnknown := (p.Options & IgnoreUnknown) != None 289 parseErr := wrapError(err) 290 291 if parseErr.Type != ErrUnknownFlag || (!ignoreUnknown && p.UnknownOptionHandler == nil) { 292 s.err = parseErr 293 break 294 } 295 296 if ignoreUnknown { 297 s.addArgs(arg) 298 } else if p.UnknownOptionHandler != nil { 299 modifiedArgs, err := p.UnknownOptionHandler(optname, strArgument{argument}, s.args) 300 301 if err != nil { 302 s.err = err 303 break 304 } 305 306 s.args = modifiedArgs 307 } 308 } 309 } 310 311 if s.err == nil { 312 p.eachOption(func(c *Command, g *Group, option *Option) { 313 err := option.clearDefault() 314 if err != nil { 315 if _, ok := err.(*Error); !ok { 316 err = p.marshalError(option, err) 317 } 318 s.err = err 319 } 320 }) 321 322 s.checkRequired(p) 323 } 324 325 var reterr error 326 327 if s.err != nil { 328 reterr = s.err 329 } else if len(s.command.commands) != 0 && !s.command.SubcommandsOptional { 330 reterr = s.estimateCommand() 331 } else if cmd, ok := s.command.data.(Commander); ok { 332 if p.CommandHandler != nil { 333 reterr = p.CommandHandler(cmd, s.retargs) 334 } else { 335 reterr = cmd.Execute(s.retargs) 336 } 337 } else if p.CommandHandler != nil { 338 reterr = p.CommandHandler(nil, s.retargs) 339 } 340 341 if reterr != nil { 342 var retargs []string 343 344 if ourErr, ok := reterr.(*Error); !ok || ourErr.Type != ErrHelp { 345 retargs = append([]string{s.arg}, s.args...) 346 } else { 347 retargs = s.args 348 } 349 350 return retargs, p.printError(reterr) 351 } 352 353 return s.retargs, nil 354} 355 356func (p *parseState) eof() bool { 357 return len(p.args) == 0 358} 359 360func (p *parseState) pop() string { 361 if p.eof() { 362 return "" 363 } 364 365 p.arg = p.args[0] 366 p.args = p.args[1:] 367 368 return p.arg 369} 370 371func (p *parseState) peek() string { 372 if p.eof() { 373 return "" 374 } 375 376 return p.args[0] 377} 378 379func (p *parseState) checkRequired(parser *Parser) error { 380 c := parser.Command 381 382 var required []*Option 383 384 for c != nil { 385 c.eachGroup(func(g *Group) { 386 for _, option := range g.options { 387 if !option.isSet && option.Required { 388 required = append(required, option) 389 } 390 } 391 }) 392 393 c = c.Active 394 } 395 396 if len(required) == 0 { 397 if len(p.positional) > 0 { 398 var reqnames []string 399 400 for _, arg := range p.positional { 401 argRequired := (!arg.isRemaining() && p.command.ArgsRequired) || arg.Required != -1 || arg.RequiredMaximum != -1 402 403 if !argRequired { 404 continue 405 } 406 407 if arg.isRemaining() { 408 if arg.value.Len() < arg.Required { 409 var arguments string 410 411 if arg.Required > 1 { 412 arguments = "arguments, but got only " + fmt.Sprintf("%d", arg.value.Len()) 413 } else { 414 arguments = "argument" 415 } 416 417 reqnames = append(reqnames, "`"+arg.Name+" (at least "+fmt.Sprintf("%d", arg.Required)+" "+arguments+")`") 418 } else if arg.RequiredMaximum != -1 && arg.value.Len() > arg.RequiredMaximum { 419 if arg.RequiredMaximum == 0 { 420 reqnames = append(reqnames, "`"+arg.Name+" (zero arguments)`") 421 } else { 422 var arguments string 423 424 if arg.RequiredMaximum > 1 { 425 arguments = "arguments, but got " + fmt.Sprintf("%d", arg.value.Len()) 426 } else { 427 arguments = "argument" 428 } 429 430 reqnames = append(reqnames, "`"+arg.Name+" (at most "+fmt.Sprintf("%d", arg.RequiredMaximum)+" "+arguments+")`") 431 } 432 } 433 } else { 434 reqnames = append(reqnames, "`"+arg.Name+"`") 435 } 436 } 437 438 if len(reqnames) == 0 { 439 return nil 440 } 441 442 var msg string 443 444 if len(reqnames) == 1 { 445 msg = fmt.Sprintf("the required argument %s was not provided", reqnames[0]) 446 } else { 447 msg = fmt.Sprintf("the required arguments %s and %s were not provided", 448 strings.Join(reqnames[:len(reqnames)-1], ", "), reqnames[len(reqnames)-1]) 449 } 450 451 p.err = newError(ErrRequired, msg) 452 return p.err 453 } 454 455 return nil 456 } 457 458 names := make([]string, 0, len(required)) 459 460 for _, k := range required { 461 names = append(names, "`"+k.String()+"'") 462 } 463 464 sort.Strings(names) 465 466 var msg string 467 468 if len(names) == 1 { 469 msg = fmt.Sprintf("the required flag %s was not specified", names[0]) 470 } else { 471 msg = fmt.Sprintf("the required flags %s and %s were not specified", 472 strings.Join(names[:len(names)-1], ", "), names[len(names)-1]) 473 } 474 475 p.err = newError(ErrRequired, msg) 476 return p.err 477} 478 479func (p *parseState) estimateCommand() error { 480 commands := p.command.sortedVisibleCommands() 481 cmdnames := make([]string, len(commands)) 482 483 for i, v := range commands { 484 cmdnames[i] = v.Name 485 } 486 487 var msg string 488 var errtype ErrorType 489 490 if len(p.retargs) != 0 { 491 c, l := closestChoice(p.retargs[0], cmdnames) 492 msg = fmt.Sprintf("Unknown command `%s'", p.retargs[0]) 493 errtype = ErrUnknownCommand 494 495 if float32(l)/float32(len(c)) < 0.5 { 496 msg = fmt.Sprintf("%s, did you mean `%s'?", msg, c) 497 } else if len(cmdnames) == 1 { 498 msg = fmt.Sprintf("%s. You should use the %s command", 499 msg, 500 cmdnames[0]) 501 } else if len(cmdnames) > 1 { 502 msg = fmt.Sprintf("%s. Please specify one command of: %s or %s", 503 msg, 504 strings.Join(cmdnames[:len(cmdnames)-1], ", "), 505 cmdnames[len(cmdnames)-1]) 506 } 507 } else { 508 errtype = ErrCommandRequired 509 510 if len(cmdnames) == 1 { 511 msg = fmt.Sprintf("Please specify the %s command", cmdnames[0]) 512 } else if len(cmdnames) > 1 { 513 msg = fmt.Sprintf("Please specify one command of: %s or %s", 514 strings.Join(cmdnames[:len(cmdnames)-1], ", "), 515 cmdnames[len(cmdnames)-1]) 516 } 517 } 518 519 return newError(errtype, msg) 520} 521 522func (p *Parser) parseOption(s *parseState, name string, option *Option, canarg bool, argument *string) (err error) { 523 if !option.canArgument() { 524 if argument != nil { 525 return newErrorf(ErrNoArgumentForBool, "bool flag `%s' cannot have an argument", option) 526 } 527 528 err = option.set(nil) 529 } else if argument != nil || (canarg && !s.eof()) { 530 var arg string 531 532 if argument != nil { 533 arg = *argument 534 } else { 535 arg = s.pop() 536 537 if validationErr := option.isValidValue(arg); validationErr != nil { 538 return newErrorf(ErrExpectedArgument, validationErr.Error()) 539 } else if p.Options&PassDoubleDash != 0 && arg == "--" { 540 return newErrorf(ErrExpectedArgument, "expected argument for flag `%s', but got double dash `--'", option) 541 } 542 } 543 544 if option.tag.Get("unquote") != "false" { 545 arg, err = unquoteIfPossible(arg) 546 } 547 548 if err == nil { 549 err = option.set(&arg) 550 } 551 } else if option.OptionalArgument { 552 option.empty() 553 554 for _, v := range option.OptionalValue { 555 err = option.set(&v) 556 557 if err != nil { 558 break 559 } 560 } 561 } else { 562 err = newErrorf(ErrExpectedArgument, "expected argument for flag `%s'", option) 563 } 564 565 if err != nil { 566 if _, ok := err.(*Error); !ok { 567 err = p.marshalError(option, err) 568 } 569 } 570 571 return err 572} 573 574func (p *Parser) marshalError(option *Option, err error) *Error { 575 s := "invalid argument for flag `%s'" 576 577 expected := p.expectedType(option) 578 579 if expected != "" { 580 s = s + " (expected " + expected + ")" 581 } 582 583 return newErrorf(ErrMarshal, s+": %s", 584 option, 585 err.Error()) 586} 587 588func (p *Parser) expectedType(option *Option) string { 589 valueType := option.value.Type() 590 591 if valueType.Kind() == reflect.Func { 592 return "" 593 } 594 595 return valueType.String() 596} 597 598func (p *Parser) parseLong(s *parseState, name string, argument *string) error { 599 if option := s.lookup.longNames[name]; option != nil { 600 // Only long options that are required can consume an argument 601 // from the argument list 602 canarg := !option.OptionalArgument 603 604 return p.parseOption(s, name, option, canarg, argument) 605 } 606 607 return newErrorf(ErrUnknownFlag, "unknown flag `%s'", name) 608} 609 610func (p *Parser) splitShortConcatArg(s *parseState, optname string) (string, *string) { 611 c, n := utf8.DecodeRuneInString(optname) 612 613 if n == len(optname) { 614 return optname, nil 615 } 616 617 first := string(c) 618 619 if option := s.lookup.shortNames[first]; option != nil && option.canArgument() { 620 arg := optname[n:] 621 return first, &arg 622 } 623 624 return optname, nil 625} 626 627func (p *Parser) parseShort(s *parseState, optname string, argument *string) error { 628 if argument == nil { 629 optname, argument = p.splitShortConcatArg(s, optname) 630 } 631 632 for i, c := range optname { 633 shortname := string(c) 634 635 if option := s.lookup.shortNames[shortname]; option != nil { 636 // Only the last short argument can consume an argument from 637 // the arguments list, and only if it's non optional 638 canarg := (i+utf8.RuneLen(c) == len(optname)) && !option.OptionalArgument 639 640 if err := p.parseOption(s, shortname, option, canarg, argument); err != nil { 641 return err 642 } 643 } else { 644 return newErrorf(ErrUnknownFlag, "unknown flag `%s'", shortname) 645 } 646 647 // Only the first option can have a concatted argument, so just 648 // clear argument here 649 argument = nil 650 } 651 652 return nil 653} 654 655func (p *parseState) addArgs(args ...string) error { 656 for len(p.positional) > 0 && len(args) > 0 { 657 arg := p.positional[0] 658 659 if err := convert(args[0], arg.value, arg.tag); err != nil { 660 p.err = err 661 return err 662 } 663 664 if !arg.isRemaining() { 665 p.positional = p.positional[1:] 666 } 667 668 args = args[1:] 669 } 670 671 p.retargs = append(p.retargs, args...) 672 return nil 673} 674 675func (p *Parser) parseNonOption(s *parseState) error { 676 if len(s.positional) > 0 { 677 return s.addArgs(s.arg) 678 } 679 680 if len(s.command.commands) > 0 && len(s.retargs) == 0 { 681 if cmd := s.lookup.commands[s.arg]; cmd != nil { 682 s.command.Active = cmd 683 cmd.fillParseState(s) 684 685 return nil 686 } else if !s.command.SubcommandsOptional { 687 s.addArgs(s.arg) 688 return newErrorf(ErrUnknownCommand, "Unknown command `%s'", s.arg) 689 } 690 } 691 692 return s.addArgs(s.arg) 693} 694 695func (p *Parser) showBuiltinHelp() error { 696 var b bytes.Buffer 697 698 p.WriteHelp(&b) 699 return newError(ErrHelp, b.String()) 700} 701 702func (p *Parser) printError(err error) error { 703 if err != nil && (p.Options&PrintErrors) != None { 704 flagsErr, ok := err.(*Error) 705 706 if ok && flagsErr.Type == ErrHelp { 707 fmt.Fprintln(os.Stdout, err) 708 } else { 709 fmt.Fprintln(os.Stderr, err) 710 } 711 } 712 713 return err 714} 715