1package flags
2
3import (
4	"bytes"
5	"fmt"
6	"os"
7	"sort"
8	"strings"
9	"unicode/utf8"
10)
11
12type parseState struct {
13	arg        string
14	args       []string
15	retargs    []string
16	positional []*Arg
17	err        error
18
19	command *Command
20	lookup  lookup
21}
22
23func (p *parseState) eof() bool {
24	return len(p.args) == 0
25}
26
27func (p *parseState) pop() string {
28	if p.eof() {
29		return ""
30	}
31
32	p.arg = p.args[0]
33	p.args = p.args[1:]
34
35	return p.arg
36}
37
38func (p *parseState) peek() string {
39	if p.eof() {
40		return ""
41	}
42
43	return p.args[0]
44}
45
46func (p *parseState) checkRequired(parser *Parser) error {
47	c := parser.Command
48
49	var required []*Option
50
51	for c != nil {
52		c.eachGroup(func(g *Group) {
53			for _, option := range g.options {
54				if !option.isSet && option.Required {
55					required = append(required, option)
56				}
57			}
58		})
59
60		c = c.Active
61	}
62
63	if len(required) == 0 {
64		if len(p.positional) > 0 && p.command.ArgsRequired {
65			var reqnames []string
66
67			for _, arg := range p.positional {
68				if arg.isRemaining() {
69					break
70				}
71
72				reqnames = append(reqnames, "`"+arg.Name+"`")
73			}
74
75			if len(reqnames) == 0 {
76				return nil
77			}
78
79			var msg string
80
81			if len(reqnames) == 1 {
82				msg = fmt.Sprintf("the required argument %s was not provided", reqnames[0])
83			} else {
84				msg = fmt.Sprintf("the required arguments %s and %s were not provided",
85					strings.Join(reqnames[:len(reqnames)-1], ", "), reqnames[len(reqnames)-1])
86			}
87
88			p.err = newError(ErrRequired, msg)
89			return p.err
90		}
91
92		return nil
93	}
94
95	names := make([]string, 0, len(required))
96
97	for _, k := range required {
98		names = append(names, "`"+k.String()+"'")
99	}
100
101	sort.Strings(names)
102
103	var msg string
104
105	if len(names) == 1 {
106		msg = fmt.Sprintf("the required flag %s was not specified", names[0])
107	} else {
108		msg = fmt.Sprintf("the required flags %s and %s were not specified",
109			strings.Join(names[:len(names)-1], ", "), names[len(names)-1])
110	}
111
112	p.err = newError(ErrRequired, msg)
113	return p.err
114}
115
116func (p *parseState) estimateCommand() error {
117	commands := p.command.sortedCommands()
118	cmdnames := make([]string, len(commands))
119
120	for i, v := range commands {
121		cmdnames[i] = v.Name
122	}
123
124	var msg string
125	var errtype ErrorType
126
127	if len(p.retargs) != 0 {
128		c, l := closestChoice(p.retargs[0], cmdnames)
129		msg = fmt.Sprintf("Unknown command `%s'", p.retargs[0])
130		errtype = ErrUnknownCommand
131
132		if float32(l)/float32(len(c)) < 0.5 {
133			msg = fmt.Sprintf("%s, did you mean `%s'?", msg, c)
134		} else if len(cmdnames) == 1 {
135			msg = fmt.Sprintf("%s. You should use the %s command",
136				msg,
137				cmdnames[0])
138		} else {
139			msg = fmt.Sprintf("%s. Please specify one command of: %s or %s",
140				msg,
141				strings.Join(cmdnames[:len(cmdnames)-1], ", "),
142				cmdnames[len(cmdnames)-1])
143		}
144	} else {
145		errtype = ErrCommandRequired
146
147		if len(cmdnames) == 1 {
148			msg = fmt.Sprintf("Please specify the %s command", cmdnames[0])
149		} else {
150			msg = fmt.Sprintf("Please specify one command of: %s or %s",
151				strings.Join(cmdnames[:len(cmdnames)-1], ", "),
152				cmdnames[len(cmdnames)-1])
153		}
154	}
155
156	return newError(errtype, msg)
157}
158
159func (p *Parser) parseOption(s *parseState, name string, option *Option, canarg bool, argument *string) (err error) {
160	if !option.canArgument() {
161		if argument != nil {
162			return newErrorf(ErrNoArgumentForBool, "bool flag `%s' cannot have an argument", option)
163		}
164
165		err = option.set(nil)
166	} else if argument != nil || (canarg && !s.eof()) {
167		var arg string
168
169		if argument != nil {
170			arg = *argument
171		} else {
172			arg = s.pop()
173			if argumentIsOption(arg) {
174				return newErrorf(ErrExpectedArgument, "expected argument for flag `%s', but got option `%s'", option, arg)
175			}
176		}
177
178		if option.tag.Get("unquote") != "false" {
179			arg, err = unquoteIfPossible(arg)
180		}
181
182		if err == nil {
183			err = option.set(&arg)
184		}
185	} else if option.OptionalArgument {
186		option.empty()
187
188		for _, v := range option.OptionalValue {
189			err = option.set(&v)
190
191			if err != nil {
192				break
193			}
194		}
195	} else {
196		err = newErrorf(ErrExpectedArgument, "expected argument for flag `%s'", option)
197	}
198
199	if err != nil {
200		if _, ok := err.(*Error); !ok {
201			err = newErrorf(ErrMarshal, "invalid argument for flag `%s' (expected %s): %s",
202				option,
203				option.value.Type(),
204				err.Error())
205		}
206	}
207
208	return err
209}
210
211func (p *Parser) parseLong(s *parseState, name string, argument *string) error {
212	if option := s.lookup.longNames[name]; option != nil {
213		// Only long options that are required can consume an argument
214		// from the argument list
215		canarg := !option.OptionalArgument
216
217		return p.parseOption(s, name, option, canarg, argument)
218	}
219
220	return newErrorf(ErrUnknownFlag, "unknown flag `%s'", name)
221}
222
223func (p *Parser) splitShortConcatArg(s *parseState, optname string) (string, *string) {
224	c, n := utf8.DecodeRuneInString(optname)
225
226	if n == len(optname) {
227		return optname, nil
228	}
229
230	first := string(c)
231
232	if option := s.lookup.shortNames[first]; option != nil && option.canArgument() {
233		arg := optname[n:]
234		return first, &arg
235	}
236
237	return optname, nil
238}
239
240func (p *Parser) parseShort(s *parseState, optname string, argument *string) error {
241	if argument == nil {
242		optname, argument = p.splitShortConcatArg(s, optname)
243	}
244
245	for i, c := range optname {
246		shortname := string(c)
247
248		if option := s.lookup.shortNames[shortname]; option != nil {
249			// Only the last short argument can consume an argument from
250			// the arguments list, and only if it's non optional
251			canarg := (i+utf8.RuneLen(c) == len(optname)) && !option.OptionalArgument
252
253			if err := p.parseOption(s, shortname, option, canarg, argument); err != nil {
254				return err
255			}
256		} else {
257			return newErrorf(ErrUnknownFlag, "unknown flag `%s'", shortname)
258		}
259
260		// Only the first option can have a concatted argument, so just
261		// clear argument here
262		argument = nil
263	}
264
265	return nil
266}
267
268func (p *parseState) addArgs(args ...string) error {
269	for len(p.positional) > 0 && len(args) > 0 {
270		arg := p.positional[0]
271
272		if err := convert(args[0], arg.value, arg.tag); err != nil {
273			return err
274		}
275
276		if !arg.isRemaining() {
277			p.positional = p.positional[1:]
278		}
279
280		args = args[1:]
281	}
282
283	p.retargs = append(p.retargs, args...)
284	return nil
285}
286
287func (p *Parser) parseNonOption(s *parseState) error {
288	if len(s.positional) > 0 {
289		return s.addArgs(s.arg)
290	}
291
292	if cmd := s.lookup.commands[s.arg]; cmd != nil {
293		s.command.Active = cmd
294		cmd.fillParseState(s)
295	} else if (p.Options & PassAfterNonOption) != None {
296		// If PassAfterNonOption is set then all remaining arguments
297		// are considered positional
298		if err := s.addArgs(s.arg); err != nil {
299			return err
300		}
301
302		if err := s.addArgs(s.args...); err != nil {
303			return err
304		}
305
306		s.args = []string{}
307	} else {
308		return s.addArgs(s.arg)
309	}
310
311	return nil
312}
313
314func (p *Parser) showBuiltinHelp() error {
315	var b bytes.Buffer
316
317	p.WriteHelp(&b)
318	return newError(ErrHelp, b.String())
319}
320
321func (p *Parser) printError(err error) error {
322	if err != nil && (p.Options&PrintErrors) != None {
323		fmt.Fprintln(os.Stderr, err)
324	}
325
326	return err
327}
328
329func (p *Parser) clearIsSet() {
330	p.eachCommand(func(c *Command) {
331		c.eachGroup(func(g *Group) {
332			for _, option := range g.options {
333				option.isSet = false
334			}
335		})
336	}, true)
337}
338