1// Copyright 2017 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
5package getopt
6
7import (
8	"fmt"
9	"strings"
10)
11
12// An Option can be either a Flag or a Value
13type Option interface {
14	// Name returns the name of the option.  If the option has been seen
15	// then the last way it was referenced (short or long) is returned
16	// otherwise if there is a short name then this will be the short name
17	// as a string, else it will be the long name.
18	Name() string
19
20	// IsFlag returns true if Option is a flag.
21	IsFlag() bool
22
23	// Seen returns true if the flag was seen.
24	Seen() bool
25
26	// Count returns the number of times the flag was seen.
27	Count() int
28
29	// String returns the last value the option was set to.
30	String() string
31
32	// Value returns the Value of the option.
33	Value() Value
34
35	// SetOptional makes the value optional.  The option and value are
36	// always a single argument.  Either --option or --option=value.  In
37	// the former case the value of the option does not change but the Set()
38	// will return true and the value returned by Count() is incremented.
39	// The short form is either -o or -ovalue.  SetOptional returns
40	// the Option
41	SetOptional() Option
42
43	// SetFlag makes the value a flag.  Flags are boolean values and
44	// normally do not taken a value.  They are set to true when seen.
45	// If a value is passed in the long form then it must be on, case
46	// insensitivinsensitive, one of "true", "false", "t", "f", "on", "off", "1", "0".
47	// SetFlag returns the Option
48	SetFlag() Option
49
50	// Reset resets the state of the option so it appears it has not
51	// yet been seen, including resetting the value of the option
52	// to its original default state.
53	Reset()
54}
55
56type option struct {
57	short    rune   // 0 means no short name
58	long     string // "" means no long name
59	isLong   bool   // True if they used the long name
60	flag     bool   // true if a boolean flag
61	defval   string // default value
62	optional bool   // true if we take an optional value
63	help     string // help message
64	where    string // file where the option was defined
65	value    Value  // current value of option
66	count    int    // number of times we have seen this option
67	name     string // name of the value (for usage)
68	uname    string // name of the option (for usage)
69}
70
71// usageName returns the name of the option for printing usage lines in one
72// of the following forms:
73//
74//  -f
75//      --flag
76//  -f, --flag
77//  -s value
78//      --set=value
79//  -s, --set=value
80func (o *option) usageName() string {
81	// Don't print help messages if we have none and there is only one
82	// way to specify the option.
83	if o.help == "" && (o.short == 0 || o.long == "") {
84		return ""
85	}
86	n := ""
87
88	switch {
89	case o.short != 0 && o.long == "":
90		n = "-" + string(o.short)
91	case o.short == 0 && o.long != "":
92		n = "    --" + o.long
93	case o.short != 0 && o.long != "":
94		n = "-" + string(o.short) + ", --" + o.long
95	}
96
97	switch {
98	case o.flag:
99		return n
100	case o.optional:
101		return n + "[=" + o.name + "]"
102	case o.long != "":
103		return n + "=" + o.name
104	}
105	return n + " " + o.name
106}
107
108// sortName returns the name to sort the option on.
109func (o *option) sortName() string {
110	if o.short != 0 {
111		return string(o.short) + o.long
112	}
113	return o.long[:1] + o.long
114}
115
116func (o *option) Seen() bool          { return o.count > 0 }
117func (o *option) Count() int          { return o.count }
118func (o *option) IsFlag() bool        { return o.flag }
119func (o *option) String() string      { return o.value.String() }
120func (o *option) SetOptional() Option { o.optional = true; return o }
121func (o *option) SetFlag() Option     { o.flag = true; return o }
122
123func (o *option) Value() Value {
124	if o == nil {
125		return nil
126	}
127	return o.value
128}
129
130func (o *option) Name() string {
131	if !o.isLong && o.short != 0 {
132		return "-" + string(o.short)
133	}
134	return "--" + o.long
135}
136
137// Reset rests an option so that it appears it has not yet been seen.
138func (o *option) Reset() {
139	o.isLong = false
140	o.count = 0
141	o.value.Set(o.defval, o)
142}
143
144type optionList []*option
145
146func (ol optionList) Len() int      { return len(ol) }
147func (ol optionList) Swap(i, j int) { ol[i], ol[j] = ol[j], ol[i] }
148func (ol optionList) Less(i, j int) bool {
149	// first check the short names (or the first letter of the long name)
150	// If they are not equal (case insensitive) then we have our answer
151	n1 := ol[i].sortName()
152	n2 := ol[j].sortName()
153	l1 := strings.ToLower(n1)
154	l2 := strings.ToLower(n2)
155	if l1 < l2 {
156		return true
157	}
158	if l2 < l1 {
159		return false
160	}
161	return n1 < n2
162}
163
164// AddOption add the option o to set CommandLine if o is not already in set
165// CommandLine.
166func AddOption(o Option) {
167	CommandLine.AddOption(o)
168}
169
170// AddOption add the option o to set s if o is not already in set s.
171func (s *Set) AddOption(o Option) {
172	opt := o.(*option)
173	for _, eopt := range s.options {
174		if opt == eopt {
175			return
176		}
177	}
178	if opt.short != 0 {
179		if oo, ok := s.shortOptions[opt.short]; ok {
180			fmt.Fprintf(stderr, "%s: -%c already declared at %s\n", opt.where, opt.short, oo.where)
181			exit(1)
182		}
183		s.shortOptions[opt.short] = opt
184	}
185	if opt.long != "" {
186		if oo, ok := s.longOptions[opt.long]; ok {
187			fmt.Fprintf(stderr, "%s: --%s already declared at %s\n", opt.where, opt.long, oo.where)
188			exit(1)
189		}
190		s.longOptions[opt.long] = opt
191	}
192	s.options = append(s.options, opt)
193}
194