parser.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  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. "os"
  7. "path"
  8. )
  9. // A Parser provides command line option parsing. It can contain several
  10. // option groups each with their own set of options.
  11. type Parser struct {
  12. // Embedded, see Command for more information
  13. *Command
  14. // A usage string to be displayed in the help message.
  15. Usage string
  16. // Option flags changing the behavior of the parser.
  17. Options Options
  18. internalError error
  19. }
  20. // Options provides parser options that change the behavior of the option
  21. // parser.
  22. type Options uint
  23. const (
  24. // None indicates no options.
  25. None Options = 0
  26. // HelpFlag adds a default Help Options group to the parser containing
  27. // -h and --help options. When either -h or --help is specified on the
  28. // command line, the parser will return the special error of type
  29. // ErrHelp. When PrintErrors is also specified, then the help message
  30. // will also be automatically printed to os.Stderr.
  31. HelpFlag = 1 << iota
  32. // PassDoubleDash passes all arguments after a double dash, --, as
  33. // remaining command line arguments (i.e. they will not be parsed for
  34. // flags).
  35. PassDoubleDash
  36. // IgnoreUnknown ignores any unknown options and passes them as
  37. // remaining command line arguments instead of generating an error.
  38. IgnoreUnknown
  39. // PrintErrors prints any errors which occurred during parsing to
  40. // os.Stderr.
  41. PrintErrors
  42. // PassAfterNonOption passes all arguments after the first non option
  43. // as remaining command line arguments. This is equivalent to strict
  44. // POSIX processing.
  45. PassAfterNonOption
  46. // Default is a convenient default set of options which should cover
  47. // most of the uses of the flags package.
  48. Default = HelpFlag | PrintErrors | PassDoubleDash
  49. )
  50. // Parse is a convenience function to parse command line options with default
  51. // settings. The provided data is a pointer to a struct representing the
  52. // default option group (named "Application Options"). For more control, use
  53. // flags.NewParser.
  54. func Parse(data interface{}) ([]string, error) {
  55. return NewParser(data, Default).Parse()
  56. }
  57. // ParseArgs is a convenience function to parse command line options with default
  58. // settings. The provided data is a pointer to a struct representing the
  59. // default option group (named "Application Options"). The args argument is
  60. // the list of command line arguments to parse. If you just want to parse the
  61. // default program command line arguments (i.e. os.Args), then use flags.Parse
  62. // instead. For more control, use flags.NewParser.
  63. func ParseArgs(data interface{}, args []string) ([]string, error) {
  64. return NewParser(data, Default).ParseArgs(args)
  65. }
  66. // NewParser creates a new parser. It uses os.Args[0] as the application
  67. // name and then calls Parser.NewNamedParser (see Parser.NewNamedParser for
  68. // more details). The provided data is a pointer to a struct representing the
  69. // default option group (named "Application Options"), or nil if the default
  70. // group should not be added. The options parameter specifies a set of options
  71. // for the parser.
  72. func NewParser(data interface{}, options Options) *Parser {
  73. ret := NewNamedParser(path.Base(os.Args[0]), options)
  74. if data != nil {
  75. _, ret.internalError = ret.AddGroup("Application Options", "", data)
  76. }
  77. return ret
  78. }
  79. // NewNamedParser creates a new parser. The appname is used to display the
  80. // executable name in the builtin help message. Option groups and commands can
  81. // be added to this parser by using AddGroup and AddCommand.
  82. func NewNamedParser(appname string, options Options) *Parser {
  83. return &Parser{
  84. Command: newCommand(appname, "", "", nil),
  85. Options: options,
  86. }
  87. }
  88. // Parse parses the command line arguments from os.Args using Parser.ParseArgs.
  89. // For more detailed information see ParseArgs.
  90. func (p *Parser) Parse() ([]string, error) {
  91. return p.ParseArgs(os.Args[1:])
  92. }
  93. // ParseArgs parses the command line arguments according to the option groups that
  94. // were added to the parser. On successful parsing of the arguments, the
  95. // remaining, non-option, arguments (if any) are returned. The returned error
  96. // indicates a parsing error and can be used with PrintError to display
  97. // contextual information on where the error occurred exactly.
  98. //
  99. // When the common help group has been added (AddHelp) and either -h or --help
  100. // was specified in the command line arguments, a help message will be
  101. // automatically printed. Furthermore, the special error type ErrHelp is returned.
  102. // It is up to the caller to exit the program if so desired.
  103. func (p *Parser) ParseArgs(args []string) ([]string, error) {
  104. if p.internalError != nil {
  105. return nil, p.internalError
  106. }
  107. p.eachCommand(func(c *Command) {
  108. p.eachGroup(func(g *Group) {
  109. g.storeDefaults()
  110. })
  111. }, true)
  112. // Add builtin help group to all commands if necessary
  113. if (p.Options & HelpFlag) != None {
  114. p.addHelpGroups(p.showBuiltinHelp)
  115. }
  116. s := &parseState{
  117. args: args,
  118. retargs: make([]string, 0, len(args)),
  119. command: p.Command,
  120. lookup: p.makeLookup(),
  121. }
  122. for !s.eof() {
  123. arg := s.pop()
  124. // When PassDoubleDash is set and we encounter a --, then
  125. // simply append all the rest as arguments and break out
  126. if (p.Options&PassDoubleDash) != None && arg == "--" {
  127. s.retargs = append(s.retargs, s.args...)
  128. break
  129. }
  130. if !argumentIsOption(arg) {
  131. // Note: this also sets s.err, so we can just check for
  132. // nil here and use s.err later
  133. if p.parseNonOption(s) != nil {
  134. break
  135. }
  136. continue
  137. }
  138. var err error
  139. var option *Option
  140. prefix, optname, islong := stripOptionPrefix(arg)
  141. optname, argument := splitOption(prefix, optname, islong)
  142. if islong {
  143. option, err = p.parseLong(s, optname, argument)
  144. } else {
  145. option, err = p.parseShort(s, optname, argument)
  146. }
  147. if err != nil {
  148. ignoreUnknown := (p.Options & IgnoreUnknown) != None
  149. parseErr := wrapError(err)
  150. if !(parseErr.Type == ErrUnknownFlag && ignoreUnknown) {
  151. s.err = parseErr
  152. break
  153. }
  154. if ignoreUnknown {
  155. s.retargs = append(s.retargs, arg)
  156. }
  157. } else {
  158. delete(s.lookup.required, option)
  159. }
  160. }
  161. if s.err == nil {
  162. s.checkRequired()
  163. }
  164. if s.err != nil {
  165. return nil, p.printError(s.err)
  166. }
  167. if len(s.command.commands) != 0 {
  168. return nil, p.printError(s.estimateCommand())
  169. } else if cmd, ok := s.command.data.(Commander); ok {
  170. return nil, p.printError(cmd.Execute(s.retargs))
  171. }
  172. return s.retargs, nil
  173. }