forked from jessevdk/go-flags
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathparser.go
220 lines (180 loc) · 6.06 KB
/
parser.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
// Copyright 2012 Jesse van den Kieboom. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flags
import (
"bytes"
"fmt"
"os"
"path"
"strings"
"unicode/utf8"
)
// A Parser provides command line option parsing. It can contain several
// option groups each with their own set of options.
type Parser struct {
// The option groups available to the parser
Groups []*Group
// The parser application name
ApplicationName string
// The usage (e.g. [OPTIONS] <filename>)
Usage string
Options Options
}
// Parser options
type Options uint
const (
// No options
None Options = 0
// Add a default Help Options group to the parser containing -h and
// --help options. When either -h or --help is specified on the command
// line, a pretty formatted help message will be printed to os.Stderr.
// The parser will return ErrHelp.
HelpFlag = 1 << iota
// Pass all arguments after a double dash, --, as remaining command line
// arguments (i.e. they will not be parsed for flags)
PassDoubleDash
// Ignore any unknown options and pass them as remaining command line
// arguments
IgnoreUnknown
// Print any errors which occured during parsing to os.Stderr
PrintErrors
// A convenient default set of options
Default = HelpFlag | PrintErrors | PassDoubleDash
)
// Parse is a convenience function to parse command line options with default
// settings. The provided data is a pointer to a struct representing the
// default option group (named "Application Options"). For more control, use
// flags.NewParser.
func Parse(data interface{}) ([]string, error) {
return NewParser(data, Default).Parse()
}
// NewParser creates a new parser. It uses os.Args[0] as the application
// name and then calls Parser.NewNamedParser (see Parser.NewNamedParser for
// more details). The provided data is a pointer to a struct representing the
// default option group (named "Application Options"), or nil if the default
// group should not be added. The options parameter specifies a set of options
// for the parser.
func NewParser(data interface{}, options Options) *Parser {
if data == nil {
return NewNamedParser(path.Base(os.Args[0]), options)
}
return NewNamedParser(path.Base(os.Args[0]), options, NewGroup("Application Options", data))
}
// NewNamedParser creates a new parser. The appname is used to display the
// executable name in the builtin help message. An initial set of option groups
// can be specified when constructing a parser, but you can also add additional
// option groups later (see Parser.AddGroup).
func NewNamedParser(appname string, options Options, groups ...*Group) *Parser {
return &Parser{
ApplicationName: appname,
Groups: groups,
Options: options,
Usage: "[OPTIONS]",
}
}
// AddGroup adds a new group to the parser with the given name and data. The
// data needs to be a pointer to a struct from which the fields indicate which
// options are in the group.
func (p *Parser) AddGroup(name string, data interface{}) *Parser {
p.Groups = append(p.Groups, NewGroup(name, data))
return p
}
// Parse parses the command line arguments from os.Args using Parser.ParseArgs.
// For more detailed information see ParseArgs.
func (p *Parser) Parse() ([]string, error) {
return p.ParseArgs(os.Args[1:])
}
// ParseArgs parses the command line arguments according to the option groups that
// were added to the parser. On successful parsing of the arguments, the
// remaining, non-option, arguments (if any) are returned. The returned error
// indicates a parsing error and can be used with PrintError to display
// contextual information on where the error occurred exactly.
//
// When the common help group has been added (AddHelp) and either -h or --help
// was specified in the command line arguments, a help message will be
// automatically printed. Furthermore, the special error type ErrHelp is returned.
// It is up to the caller to exit the program if so desired.
func (p *Parser) ParseArgs(args []string) ([]string, error) {
ret := make([]string, 0, len(args))
i := 0
if (p.Options & HelpFlag) != None {
var help struct {
ShowHelp func() error `short:"h" long:"help" description:"Show this help message"`
}
help.ShowHelp = func() error {
var b bytes.Buffer
p.WriteHelp(&b)
return newError(ErrHelp, b.String())
}
p.Groups = append([]*Group{NewGroup("Help Options", &help)}, p.Groups...)
p.Options &^= HelpFlag
}
for i < len(args) {
arg := args[i]
i++
// When PassDoubleDash is set and we encounter a --, then
// simply append all the rest as arguments and break out
if (p.Options&PassDoubleDash) != None && arg == "--" {
ret = append(ret, args[i:]...)
break
}
// If the argument is not an option, then append it to the rest
if arg[0] != '-' {
ret = append(ret, arg)
continue
}
pos := strings.Index(arg, "=")
var argument *string
if pos >= 0 {
rest := arg[pos+1:]
argument = &rest
arg = arg[:pos]
}
var err error
if strings.HasPrefix(arg, "--") {
err, i = p.parseLong(args, arg[2:], argument, i)
} else {
short := arg[1:]
for j, c := range short {
clen := utf8.RuneLen(c)
islast := (j+clen == len(short))
if !islast && argument == nil {
rr := short[j+clen:]
next, _ := utf8.DecodeRuneInString(rr)
info, _ := p.getShort(c)
if info != nil && info.canArgument() {
if snext, _ := p.getShort(next); snext == nil {
// Consider the next stuff as an argument
argument = &rr
islast = true
}
}
}
err, i = p.parseShort(args, c, islast, argument, i)
if err != nil || islast {
break
}
}
}
if err != nil {
if (p.Options & IgnoreUnknown) != None {
ret = append(ret, arg)
} else {
parseErr, ok := err.(*Error)
if !ok {
parseErr = newError(ErrUnknown, err.Error())
}
if (p.Options&PrintErrors) != None {
if parseErr.Type == ErrHelp {
fmt.Fprintln(os.Stderr, err)
} else {
fmt.Fprintf(os.Stderr, "Flags error: %s\n", err.Error())
}
}
return nil, err
}
}
}
return ret, nil
}