1// Copyright 2013 Google Inc.  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
5// Package getopt (v1) provides traditional getopt processing for implementing
6// commands that use traditional command lines.  The standard Go flag package
7// cannot be used to write a program that parses flags the way ls or ssh does,
8// for example.
9//
10// A new version of this package (v2) (whose package name is also getopt) is
11// available as:
12//
13//	"github.com/pborman/getopt/v2"
14//
15// Getopt supports functionality found in both the standard BSD getopt as well
16// as (one of the many versions of) the GNU getopt_long.  Being a Go package,
17// this package makes common usage easy, but still enables more controlled usage
18// if needed.
19//
20// Typical usage:
21//
22//	// Declare the flags to be used
23//	helpFlag := getopt.Bool('?', "display help")
24//	cmdFlag := getopt.StringLong("command", 'c', "", "the command")
25//
26//	func main() {
27//		// Parse the program arguments
28//		getopt.Parse()
29//		// Get the remaining positional parameters
30//		args := getopt.Args()
31//
32// If you don't want the program to exit on error, use getopt.Getopt:
33//
34//		err := getopt.Getopt(nil)
35//		if err != nil {
36//			// code to handle error
37//			fmt.Fprintln(os.Stderr, err)
38//		}
39//
40// Support is provided for both short (-f) and long (--flag) options.  A single
41// option may have both a short and a long name.  Each option may be a flag or a
42// value.  A value takes an argument.
43//
44// Declaring no long names causes this package to process arguments like the
45// traditional BSD getopt.
46//
47// Short flags may be combined into a single parameter.  For example, "-a -b -c"
48// may also be expressed "-abc".  Long flags must stand on their own "--alpha
49// --beta"
50//
51// Values require an argument.  For short options the argument may either be
52// immediately following the short name or as the next argument.  Only one short
53// value may be combined with short flags in a single argument; the short value
54// must be after all short flags.  For example, if f is a flag and v is a value,
55// then:
56//
57//  -vvalue    (sets v to "value")
58//  -v value   (sets v to "value")
59//  -fvvalue   (sets f, and sets v to "value")
60//  -fv value  (sets f, and sets v to "value")
61//  -vf value  (set v to "f" and value is the first parameter)
62//
63// For the long value option val:
64//
65//  --val value (sets val to "value")
66//  --val=value (sets val to "value")
67//  --valvalue  (invalid option "valvalue")
68//
69// Values with an optional value only set the value if the value is part of the
70// same argument.  In any event, the option count is increased and the option is
71// marked as seen.
72//
73//  -v -f          (sets v and f as being seen)
74//  -vvalue -f     (sets v to "value" and sets f)
75//  --val -f       (sets v and f as being seen)
76//  --val=value -f (sets v to "value" and sets f)
77//
78// There is no convience function defined for making the value optional.  The
79// SetOptional method must be called on the actual Option.
80//
81//  v := String("val", 'v', "", "the optional v")
82//  Lookup("v").SetOptional()
83//
84//  var s string
85//  StringVar(&s, "val", 'v', "the optional v).SetOptional()
86//
87// Parsing continues until the first non-option or "--" is encountered.
88//
89// The short name "-" can be used, but it either is specified as "-" or as part
90// of a group of options, for example "-f-".  If there are no long options
91// specified then "--f" could also be used.  If "-" is not declared as an option
92// then the single "-" will also terminate the option processing but unlike
93// "--", the "-" will be part of the remaining arguments.
94//
95// Normally the parsing is performed by calling the Parse function.  If it is
96// important to see the order of the options then the Getopt function should be
97// used.  The standard Parse function does the equivalent of:
98//
99// func Parse() {
100//	if err := getopt.Getopt(os.Args, nil); err != nil {
101//		fmt.Fprintln(os.Stderr, err)
102//		s.usage()
103//		os.Exit(1)
104//	}
105//
106// When calling Getopt it is the responsibility of the caller to print any
107// errors.
108//
109// Normally the default option set, CommandLine, is used.  Other option sets may
110// be created with New.
111//
112// After parsing, the sets Args will contain the non-option arguments.  If an
113// error is encountered then Args will begin with argument that caused the
114// error.
115//
116// It is valid to call a set's Parse a second time to amend the current set of
117// flags or values.  As an example:
118//
119//  var a = getopt.Bool('a', "", "The a flag")
120//  var b = getopt.Bool('b', "", "The a flag")
121//  var cmd = ""
122//
123//  var opts = getopt.CommandLine
124//
125//  opts.Parse(os.Args)
126//  if opts.NArgs() > 0 {
127//      cmd = opts.Arg(0)
128//      opts.Parse(opts.Args())
129//  }
130//
131// If called with set to { "prog", "-a", "cmd", "-b", "arg" } then both and and
132// b would be set, cmd would be set to "cmd", and opts.Args() would return {
133// "arg" }.
134//
135// Unless an option type explicitly prohibits it, an option may appear more than
136// once in the arguments.  The last value provided to the option is the value.
137//
138// SYNTAX
139//
140// For each option type there are an unfortunately large number of ways, 8, to
141// initialize the option.  This number is derived from three attributes:
142//
143//  1)  Short or Long name
144//  2)  Normal vs Var
145//  3)  Command Line vs Option Set
146//
147// The first two variations provide 4 signature:
148//
149//  Option(name rune, [value type,]  helpvalue... string)
150//  OptionLong(name string, short rune, [value type,]  helpvalue... string)
151//  OptionVar(p *type, name rune, helpvalue... string)
152//  OptionVarLong(p *type, name string, short rune, helpvalue... string)
153//
154// Foo can actually be expressed in terms of FooLong:
155//
156//  func Foo(name rune, value type, helpvalue... string) *type {
157//      return FooLong("", name, value, helpvalue...)
158//  }
159//
160// Normally Foo is used, unless long options are needed.  Setting short to 0
161// creates only a long option.
162//
163// The difference bentween Foo and FooVar is that you pass a pointer, p, to the
164// location of the value to FooVar.  The default value is simply *p.  The
165// initial value of *p is the defaut value of the option.
166//
167// Foo is actually a wrapper around FooVar:
168//
169//  func Foo(name rune, value type, helpvalue... string) *type {
170//      p := value
171//      FooVar(&p, name, helpvalue... string)
172//      return &p
173//  }
174//
175//
176// The third variation provides a top-level function and a method on a Set:
177//
178//  func Option(...)
179//  func (s *Set) Option(...)
180//
181// The top-level function is simply:
182//
183//  func Option(...) *type {
184//      return CommandLine.Option(...) {
185//  }
186//
187// To simplfy documentation, typically only the main top-level function is fully
188// documented.  The others will have documentation when there is something
189// special about them.
190//
191// VALUEHELP
192//
193// All non-flag options are created with a "valuehelp" as the last parameter.
194// Valuehelp should be 0, 1, or 2 strings.  The first string, if provided, is
195// the usage message for the option.  If the second string, if provided, is the
196// name to use for the value when displaying the usage.  If not provided the
197// term "value" is assumed.
198//
199// The usage message for the option created with
200//
201//  StringLong("option", 'o', "defval", "a string of letters")
202//
203// is
204//
205//  -o, -option=value
206//
207//  StringLong("option", 'o', "defval", "a string of letters", "string")
208//
209// is
210//
211//  -o, -option=string
212package getopt
213
214import (
215	"fmt"
216	"io"
217	"os"
218	"path"
219	"sort"
220	"strings"
221)
222
223// stderr allows tests to capture output to standard error.
224var stderr io.Writer = os.Stderr
225
226// exit allows tests to capture an os.Exit call
227var exit = os.Exit
228
229// DisplayWidth is used to determine where to split usage long lines.
230var DisplayWidth = 80
231
232// HelpColumn is the maximum column position that help strings start to display
233// at.  If the option usage is too long then the help string will be displayed
234// on the next line.  For example:
235//
236//   -a   this is the a flag
237//   -u, --under=location
238//        the u flag's usage is quite long
239var HelpColumn = 20
240
241// PrintUsage prints the usage of the program to w.
242func (s *Set) PrintUsage(w io.Writer) {
243	sort.Sort(s.options)
244	flags := ""
245
246	// Build up the list of short flag names and also compute
247	// how to display the option in the longer help listing.
248	// We also keep track of the longest option usage string
249	// that is no more than HelpColumn-3 bytes (at which point
250	// we use two lines to display the help).  The three
251	// is for the leading space and the two spaces before the
252	// help string.
253	for _, opt := range s.options {
254		if opt.name == "" {
255			opt.name = "value"
256		}
257		if opt.uname == "" {
258			opt.uname = opt.usageName()
259		}
260		if opt.flag && opt.short != 0 && opt.short != '-' {
261			flags += string(opt.short)
262		}
263	}
264
265	var opts []string
266
267	// The short option - is special
268	if s.shortOptions['-'] != nil {
269		opts = append(opts, "-")
270	}
271
272	// If we have a bundle of flags, add them to the list
273	if flags != "" {
274		opts = append(opts, "-"+flags)
275	}
276
277	// Now append all the long options and options that require
278	// values.
279	for _, opt := range s.options {
280		if opt.flag {
281			if opt.short != 0 {
282				continue
283			}
284			flags = "--" + opt.long
285		} else if opt.short != 0 {
286			flags = "-" + string(opt.short) + " " + opt.name
287		} else {
288			flags = "--" + string(opt.long) + " " + opt.name
289		}
290		opts = append(opts, flags)
291	}
292	flags = strings.Join(opts, "] [")
293	if flags != "" {
294		flags = " [" + flags + "]"
295	}
296	if s.parameters != "" {
297		flags += " " + s.parameters
298	}
299	fmt.Fprintf(w, "Usage: %s%s\n", s.program, flags)
300	s.PrintOptions(w)
301}
302
303// PrintOptions prints the list of options in s to w.
304func (s *Set) PrintOptions(w io.Writer) {
305	sort.Sort(s.options)
306	max := 4
307	for _, opt := range s.options {
308		if opt.name == "" {
309			opt.name = "value"
310		}
311		if opt.uname == "" {
312			opt.uname = opt.usageName()
313		}
314		if max < len(opt.uname) && len(opt.uname) <= HelpColumn-3 {
315			max = len(opt.uname)
316		}
317	}
318	// Now print one or more usage lines per option.
319	for _, opt := range s.options {
320		if opt.uname != "" {
321			opt.help = strings.TrimSpace(opt.help)
322			if len(opt.help) == 0 {
323				fmt.Fprintf(w, " %s\n", opt.uname)
324				continue
325			}
326			help := strings.Split(opt.help, "\n")
327			// If they did not put in newlines then we will insert
328			// them to keep the help messages from wrapping.
329			if len(help) == 1 {
330				help = breakup(help[0], DisplayWidth-HelpColumn)
331			}
332			if len(opt.uname) <= max {
333				fmt.Fprintf(w, " %-*s  %s\n", max, opt.uname, help[0])
334				help = help[1:]
335			} else {
336				fmt.Fprintf(w, " %s\n", opt.uname)
337			}
338			for _, s := range help {
339				fmt.Fprintf(w, " %-*s  %s\n", max, " ", s)
340			}
341		}
342	}
343}
344
345// breakup breaks s up into strings no longer than max bytes.
346func breakup(s string, max int) []string {
347	var a []string
348
349	for {
350		// strip leading spaces
351		for len(s) > 0 && s[0] == ' ' {
352			s = s[1:]
353		}
354		// If the option is no longer than the max just return it
355		if len(s) <= max {
356			if len(s) != 0 {
357				a = append(a, s)
358			}
359			return a
360		}
361		x := max
362		for s[x] != ' ' {
363			// the first word is too long?!
364			if x == 0 {
365				x = max
366				for x < len(s) && s[x] != ' ' {
367					x++
368				}
369				if x == len(s) {
370					x--
371				}
372				break
373			}
374			x--
375		}
376		for s[x] == ' ' {
377			x--
378		}
379		a = append(a, s[:x+1])
380		s = s[x+1:]
381	}
382}
383
384// Parse uses Getopt to parse args using the options set for s.  The first
385// element of args is used to assign the program for s if it is not yet set.  On
386// error, Parse displays the error message as well as a usage message on
387// standard error and then exits the program.
388func (s *Set) Parse(args []string) {
389	if err := s.Getopt(args, nil); err != nil {
390		fmt.Fprintln(stderr, err)
391		s.usage()
392		exit(1)
393	}
394}
395
396// Parse uses Getopt to parse args using the options set for s.  The first
397// element of args is used to assign the program for s if it is not yet set.
398// Getop calls fn, if not nil, for each option parsed.
399//
400// Getopt returns nil when all options have been processed (a non-option
401// argument was encountered, "--" was encountered, or fn returned false).
402//
403// On error getopt returns a refernce to an InvalidOption (which implements
404// the error interface).
405func (s *Set) Getopt(args []string, fn func(Option) bool) (err error) {
406	s.State = InProgress
407	defer func() {
408		if s.State == InProgress {
409			switch {
410			case err != nil:
411				s.State = Failure
412			case len(s.args) == 0:
413				s.State = EndOfArguments
414			default:
415				s.State = Unknown
416			}
417		}
418	}()
419	if fn == nil {
420		fn = func(Option) bool { return true }
421	}
422	if len(args) == 0 {
423		return nil
424	}
425
426	if s.program == "" {
427		s.program = path.Base(args[0])
428	}
429	args = args[1:]
430Parsing:
431	for len(args) > 0 {
432		arg := args[0]
433		s.args = args
434		args = args[1:]
435
436		// end of options?
437		if arg == "" || arg[0] != '-' {
438			s.State = EndOfOptions
439			return nil
440		}
441
442		if arg == "-" {
443			goto ShortParsing
444		}
445
446		// explicitly request end of options?
447		if arg == "--" {
448			s.args = args
449			s.State = DashDash
450			return nil
451		}
452
453		// Long option processing
454		if len(s.longOptions) > 0 && arg[1] == '-' {
455			e := strings.IndexRune(arg, '=')
456			var value string
457			if e > 0 {
458				value = arg[e+1:]
459				arg = arg[:e]
460			}
461			opt := s.longOptions[arg[2:]]
462			// If we are processing long options then --f is -f
463			// if f is not defined as a long option.
464			// This lets you say --f=false
465			if opt == nil && len(arg[2:]) == 1 {
466				opt = s.shortOptions[rune(arg[2])]
467			}
468			if opt == nil {
469				return unknownOption(arg[2:])
470			}
471			opt.isLong = true
472			// If we require an option and did not have an =
473			// then use the next argument as an option.
474			if !opt.flag && e < 0 && !opt.optional {
475				if len(args) == 0 {
476					return missingArg(opt)
477				}
478				value = args[0]
479				args = args[1:]
480			}
481			opt.count++
482
483			if err := opt.value.Set(value, opt); err != nil {
484				return setError(opt, value, err)
485			}
486
487			if !fn(opt) {
488				s.State = Terminated
489				return nil
490			}
491			continue Parsing
492		}
493
494		// Short option processing
495		arg = arg[1:] // strip -
496	ShortParsing:
497		for i, c := range arg {
498			opt := s.shortOptions[c]
499			if opt == nil {
500				// In traditional getopt, if - is not registered
501				// as an option, a lone - is treated as
502				// if there were a -- in front of it.
503				if arg == "-" {
504					s.State = Dash
505					return nil
506				}
507				return unknownOption(c)
508			}
509			opt.isLong = false
510			opt.count++
511			var value string
512			if !opt.flag {
513				value = arg[1+i:]
514				if value == "" && !opt.optional {
515					if len(args) == 0 {
516						return missingArg(opt)
517					}
518					value = args[0]
519					args = args[1:]
520				}
521			}
522			if err := opt.value.Set(value, opt); err != nil {
523				return setError(opt, value, err)
524			}
525			if !fn(opt) {
526				s.State = Terminated
527				return nil
528			}
529			if !opt.flag {
530				continue Parsing
531			}
532		}
533	}
534	s.args = []string{}
535	return nil
536}
537