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