本站源代码
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.

701 line
18KB

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