├── .travis.yml ├── AUTHORS ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── bool.go ├── bool_test.go ├── breakup_test.go ├── counter.go ├── counter_test.go ├── duration.go ├── duration_test.go ├── enum.go ├── enum_test.go ├── error.go ├── getopt.go ├── go.mod ├── int.go ├── int16.go ├── int16_test.go ├── int32.go ├── int32_test.go ├── int64.go ├── int64_test.go ├── int_test.go ├── list.go ├── list_test.go ├── option.go ├── set.go ├── signed.go ├── signed_test.go ├── string.go ├── string_test.go ├── uint.go ├── uint16.go ├── uint16_test.go ├── uint32.go ├── uint32_test.go ├── uint64.go ├── uint64_test.go ├── uint_test.go ├── unsigned.go ├── unsigned_test.go ├── util_test.go ├── v2 ├── bool.go ├── bool_test.go ├── breakup_test.go ├── counter.go ├── counter_test.go ├── duration.go ├── duration_test.go ├── enum.go ├── enum_test.go ├── error.go ├── generic.go ├── generic_test.go ├── getopt.go ├── getopt_test.go ├── go.mod ├── help_test.go ├── int.go ├── int_test.go ├── list.go ├── list_test.go ├── option.go ├── set.go ├── signed.go ├── signed_test.go ├── string.go ├── string_test.go ├── unsigned.go ├── unsigned_test.go ├── util_test.go └── var.go └── var.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - "1.11" 5 | - "1.12" 6 | - tip 7 | 8 | script: 9 | - go test -v ./... 10 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Paul Borman 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | We definitely welcome patches and contribution to this project! 4 | 5 | ### Legal requirements 6 | 7 | In order to protect both you and ourselves, you will need to sign the 8 | [Contributor License Agreement](https://cla.developers.google.com/clas). 9 | 10 | You may have already signed it for other Google projects. 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Google Inc. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google, nor the names of other 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # getopt ![build status](https://travis-ci.org/pborman/getopt.svg?branch=master) 2 | 3 | Package getopt provides traditional getopt processing for implementing 4 | commands that use traditional command lines. The standard Go flag package 5 | cannot be used to write a program that parses flags the way ls or ssh does, 6 | for example. There are two versions, v1 and v2, both named getopt, that 7 | use the following import paths: 8 | 9 | ``` 10 | "github.com/pborman/getopt" // version 1 11 | "github.com/pborman/getopt/v2" // version 2 12 | ``` 13 | 14 | This README describes version 2 of the package, which has a simplified API. 15 | 16 | ## Usage 17 | 18 | Getopt supports functionality found in both the standard BSD getopt as well 19 | as (one of the many versions of) the GNU getopt_long. Being a Go package, 20 | this package makes common usage easy, but still enables more controlled usage 21 | if needed. 22 | 23 | Typical usage: 24 | 25 | ``` 26 | Declare flags and have getopt return pointers to the values. 27 | helpFlag := getopt.Bool('?', "display help") 28 | cmdFlag := getopt.StringLong("command", 'c', "default", "the command") 29 | 30 | Declare flags against existing variables. 31 | var ( 32 | fileName = "/the/default/path" 33 | timeout = time.Second * 5 34 | verbose bool 35 | ) 36 | func init() { 37 | getopt.Flag(&verbose, 'v', "be verbose") 38 | getopt.FlagLong(&fileName, "path", 0, "the path") 39 | getopt.FlagLong(&timeout, "timeout", 't', "some timeout") 40 | } 41 | 42 | func main() { 43 | Parse the program arguments 44 | getopt.Parse() 45 | Get the remaining positional parameters 46 | args := getopt.Args() 47 | ... 48 | ``` 49 | 50 | If you don't want the program to exit on error, use getopt.Getopt: 51 | 52 | ``` 53 | err := getopt.Getopt(nil) 54 | if err != nil { 55 | code to handle error 56 | fmt.Fprintln(os.Stderr, err) 57 | } 58 | ``` 59 | 60 | ## Flag Syntax 61 | 62 | Support is provided for both short (-f) and long (--flag) options. A single 63 | option may have both a short and a long name. Each option may be a flag or a 64 | value. A value takes an argument. 65 | 66 | Declaring no long names causes this package to process arguments like the 67 | traditional BSD getopt. 68 | 69 | Short flags may be combined into a single parameter. For example, "-a -b -c" 70 | may also be expressed "-abc". Long flags must stand on their own "--alpha 71 | --beta" 72 | 73 | Values require an argument. For short options the argument may either be 74 | immediately following the short name or as the next argument. Only one short 75 | value may be combined with short flags in a single argument; the short value 76 | must be after all short flags. For example, if f is a flag and v is a value, 77 | then: 78 | 79 | ``` 80 | -vvalue (sets v to "value") 81 | -v value (sets v to "value") 82 | -fvvalue (sets f, and sets v to "value") 83 | -fv value (sets f, and sets v to "value") 84 | -vf value (set v to "f" and value is the first parameter) 85 | ``` 86 | 87 | For the long value option val: 88 | 89 | ``` 90 | --val value (sets val to "value") 91 | --val=value (sets val to "value") 92 | --valvalue (invalid option "valvalue") 93 | ``` 94 | 95 | Values with an optional value only set the value if the value is part of the 96 | same argument. In any event, the option count is increased and the option is 97 | marked as seen. 98 | 99 | ``` 100 | -v -f (sets v and f as being seen) 101 | -vvalue -f (sets v to "value" and sets f) 102 | --val -f (sets v and f as being seen) 103 | --val=value -f (sets v to "value" and sets f) 104 | ``` 105 | 106 | There is no convience function defined for making the value optional. The 107 | SetOptional method must be called on the actual Option. 108 | 109 | ``` 110 | v := String("val", 'v', "", "the optional v") 111 | Lookup("v").SetOptional() 112 | 113 | var s string 114 | FlagLong(&s, "val", 'v', "the optional v).SetOptional() 115 | ``` 116 | 117 | Parsing continues until the first non-option or "--" is encountered. 118 | 119 | The short name "-" can be used, but it either is specified as "-" or as part 120 | of a group of options, for example "-f-". If there are no long options 121 | specified then "--f" could also be used. If "-" is not declared as an option 122 | then the single "-" will also terminate the option processing but unlike 123 | "--", the "-" will be part of the remaining arguments. 124 | 125 | ## Advanced Usage 126 | 127 | Normally the parsing is performed by calling the Parse function. If it is 128 | important to see the order of the options then the Getopt function should be 129 | used. The standard Parse function does the equivalent of: 130 | 131 | ``` 132 | func Parse() { 133 | if err := getopt.Getopt(os.Args, nil); err != nil { 134 | fmt.Fprintln(os.Stderr, err) 135 | s.usage() 136 | os.Exit(1) 137 | } 138 | } 139 | ``` 140 | 141 | When calling Getopt it is the responsibility of the caller to print any 142 | errors. 143 | 144 | Normally the default option set, CommandLine, is used. Other option sets may 145 | be created with New. 146 | 147 | After parsing, the sets Args will contain the non-option arguments. If an 148 | error is encountered then Args will begin with argument that caused the 149 | error. 150 | 151 | It is valid to call a set's Parse a second time to amend the current set of 152 | flags or values. As an example: 153 | 154 | ``` 155 | var a = getopt.Bool('a', "", "The a flag") 156 | var b = getopt.Bool('b', "", "The a flag") 157 | var cmd = "" 158 | 159 | var opts = getopt.CommandLine 160 | 161 | opts.Parse(os.Args) 162 | if opts.NArgs() > 0 { 163 | cmd = opts.Arg(0) 164 | opts.Parse(opts.Args()) 165 | } 166 | ``` 167 | 168 | If called with set to { "prog", "-a", "cmd", "-b", "arg" } then both a and 169 | b would be set, cmd would be set to "cmd", and opts.Args() would return { 170 | "arg" }. 171 | 172 | Unless an option type explicitly prohibits it, an option may appear more than 173 | once in the arguments. The last value provided to the option is the value. 174 | 175 | ## Builtin Types 176 | 177 | The Flag and FlagLong functions support most standard Go types. For the 178 | list, see the description of FlagLong below for a list of supported types. 179 | 180 | There are also helper routines to allow single line flag declarations. These 181 | types are: Bool, Counter, Duration, Enum, Int16, Int32, Int64, Int, List, 182 | Signed, String, Uint16, Uint32, Uint64, Uint, and Unsigned. 183 | 184 | Each comes in a short and long flavor, e.g., Bool and BoolLong and include 185 | functions to set the flags on the standard command line or for a specific Set 186 | of flags. 187 | 188 | Except for the Counter, Enum, Signed and Unsigned types, all of these types 189 | can be declared using Flag and FlagLong by passing in a pointer to the 190 | appropriate type. 191 | 192 | ## Declaring New Flag Types 193 | 194 | A pointer to any type that implements the Value interface may be passed to 195 | Flag or FlagLong. 196 | 197 | ## VALUEHELP 198 | 199 | All non-flag options are created with a "valuehelp" as the last parameter. 200 | Valuehelp should be 0, 1, or 2 strings. The first string, if provided, is 201 | the usage message for the option. If the second string, if provided, is the 202 | name to use for the value when displaying the usage. If not provided the 203 | term "value" is assumed. 204 | 205 | The usage message for the option created with 206 | 207 | ``` 208 | StringLong("option", 'o', "defval", "a string of letters") 209 | ``` 210 | 211 | is 212 | 213 | ``` 214 | -o, -option=value 215 | ``` 216 | while the usage message for the option created with 217 | 218 | ``` 219 | StringLong("option", 'o', "defval", "a string of letters", "string") 220 | ``` 221 | 222 | is 223 | 224 | ``` 225 | -o, -option=string 226 | ``` 227 | -------------------------------------------------------------------------------- /bool.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | ) 11 | 12 | type boolValue bool 13 | 14 | func (b *boolValue) Set(value string, opt Option) error { 15 | switch strings.ToLower(value) { 16 | case "", "1", "true", "on", "t": 17 | *b = true 18 | case "0", "false", "off", "f": 19 | *b = false 20 | default: 21 | return fmt.Errorf("invalid value for bool %s: %q", opt.Name(), value) 22 | } 23 | return nil 24 | } 25 | 26 | func (b *boolValue) String() string { 27 | if *b { 28 | return "true" 29 | } 30 | return "false" 31 | } 32 | 33 | // Bool creates a flag option that is a bool. Bools normally do not take a 34 | // value however one can be assigned by using the long form of the option: 35 | // 36 | // --option=true 37 | // --o=false 38 | // 39 | // Its value is case insenstive and one of true, false, t, f, on, off, t and 0. 40 | func Bool(name rune, helpvalue ...string) *bool { 41 | return CommandLine.Bool(name, helpvalue...) 42 | } 43 | 44 | func (s *Set) Bool(name rune, helpvalue ...string) *bool { 45 | var p bool 46 | s.BoolVarLong(&p, "", name, helpvalue...) 47 | return &p 48 | } 49 | 50 | func BoolLong(name string, short rune, helpvalue ...string) *bool { 51 | return CommandLine.BoolLong(name, short, helpvalue...) 52 | } 53 | 54 | func (s *Set) BoolLong(name string, short rune, helpvalue ...string) *bool { 55 | var p bool 56 | s.BoolVarLong(&p, name, short, helpvalue...) 57 | return &p 58 | } 59 | 60 | func BoolVar(p *bool, name rune, helpvalue ...string) Option { 61 | return CommandLine.BoolVar(p, name, helpvalue...) 62 | } 63 | 64 | func (s *Set) BoolVar(p *bool, name rune, helpvalue ...string) Option { 65 | return s.BoolVarLong(p, "", name, helpvalue...) 66 | } 67 | 68 | func BoolVarLong(p *bool, name string, short rune, helpvalue ...string) Option { 69 | return CommandLine.BoolVarLong(p, name, short, helpvalue...) 70 | } 71 | 72 | func (s *Set) BoolVarLong(p *bool, name string, short rune, helpvalue ...string) Option { 73 | return s.VarLong((*boolValue)(p), name, short, helpvalue...).SetFlag() 74 | } 75 | -------------------------------------------------------------------------------- /bool_test.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var boolTests = []struct { 14 | where string 15 | in []string 16 | f bool 17 | fc int 18 | opt bool 19 | optc int 20 | err string 21 | }{ 22 | { 23 | loc(), 24 | []string{}, 25 | false, 0, 26 | false, 0, 27 | "", 28 | }, 29 | { 30 | loc(), 31 | []string{"test", "-f", "--opt"}, 32 | true, 1, 33 | true, 1, 34 | "", 35 | }, 36 | { 37 | loc(), 38 | []string{"test", "--f", "--opt"}, 39 | true, 1, 40 | true, 1, 41 | "", 42 | }, 43 | { 44 | loc(), 45 | []string{"test", "-ff", "-f", "--opt", "--opt"}, 46 | true, 3, 47 | true, 2, 48 | "", 49 | }, 50 | { 51 | loc(), 52 | []string{"test", "--opt", "--opt=false"}, 53 | false, 0, 54 | false, 2, 55 | "", 56 | }, 57 | { 58 | loc(), 59 | []string{"test", "-f", "false"}, 60 | true, 1, 61 | false, 0, 62 | "", 63 | }, 64 | { 65 | loc(), 66 | []string{"test", "-f=false"}, 67 | true, 1, 68 | false, 0, 69 | "test: unknown option: -=\n", 70 | }, 71 | { 72 | loc(), 73 | []string{"test", "-f", "false"}, 74 | true, 1, 75 | false, 0, 76 | "", 77 | }, 78 | } 79 | 80 | func TestBool(t *testing.T) { 81 | for x, tt := range boolTests { 82 | reset() 83 | f := Bool('f') 84 | opt := BoolLong("opt", 0) 85 | if strings.Index(tt.where, ":-") > 0 { 86 | tt.where = fmt.Sprintf("#%d", x) 87 | } 88 | 89 | parse(tt.in) 90 | if s := checkError(tt.err); s != "" { 91 | t.Errorf("%s: %s", tt.where, s) 92 | } 93 | if got, want := *f, tt.f; got != want { 94 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 95 | } 96 | if got, want := *opt, tt.opt; got != want { 97 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 98 | } 99 | if got, want := GetCount('f'), tt.fc; got != want { 100 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 101 | } 102 | if got, want := GetCount("opt"), tt.optc; got != want { 103 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /breakup_test.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "testing" 9 | ) 10 | 11 | var breakupTests = []struct { 12 | in string 13 | max int 14 | out []string 15 | }{ 16 | {"", 8, []string{}}, 17 | {"a fox", 8, []string{"a fox"}}, 18 | {"a foxhound is sly", 2, []string{"a", "foxhound", "is", "sly"}}, 19 | {"a foxhound is sly", 5, []string{"a", "foxhound", "is", "sly"}}, 20 | {"a foxhound is sly", 6, []string{"a", "foxhound", "is sly"}}, 21 | {"a foxhound is sly", 7, []string{"a", "foxhound", "is sly"}}, 22 | {"a foxhound is sly", 8, []string{"a", "foxhound", "is sly"}}, 23 | {"a foxhound is sly", 9, []string{"a", "foxhound", "is sly"}}, 24 | {"a foxhound is sly", 10, []string{"a foxhound", "is sly"}}, 25 | } 26 | 27 | func TestBreakup(t *testing.T) { 28 | for x, tt := range breakupTests { 29 | out := breakup(tt.in, tt.max) 30 | if badSlice(out, tt.out) { 31 | t.Errorf("#%d: got %v, want %v", x, out, tt.out) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /counter.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | type counterValue int 13 | 14 | func (b *counterValue) Set(value string, opt Option) error { 15 | if value == "" { 16 | *b++ 17 | } else { 18 | v, err := strconv.ParseInt(value, 0, strconv.IntSize) 19 | if err != nil { 20 | if e, ok := err.(*strconv.NumError); ok { 21 | switch e.Err { 22 | case strconv.ErrRange: 23 | err = fmt.Errorf("value out of range: %s", value) 24 | case strconv.ErrSyntax: 25 | err = fmt.Errorf("not a valid number: %s", value) 26 | } 27 | } 28 | return err 29 | } 30 | *b = counterValue(v) 31 | } 32 | return nil 33 | } 34 | 35 | func (b *counterValue) String() string { 36 | return strconv.Itoa(int(*b)) 37 | } 38 | 39 | // Counter creates a counting flag stored as an int. Each time the option 40 | // is seen while parsing the value is incremented. The value of the counter 41 | // may be explicitly set by using the long form: 42 | // 43 | // --counter=5 44 | // --c=5 45 | // 46 | // Further instances of the option will increment from the set value. 47 | func Counter(name rune, helpvalue ...string) *int { 48 | return CommandLine.Counter(name, helpvalue...) 49 | } 50 | 51 | func (s *Set) Counter(name rune, helpvalue ...string) *int { 52 | var p int 53 | s.CounterVarLong(&p, "", name, helpvalue...) 54 | return &p 55 | } 56 | 57 | func CounterLong(name string, short rune, helpvalue ...string) *int { 58 | return CommandLine.CounterLong(name, short, helpvalue...) 59 | } 60 | 61 | func (s *Set) CounterLong(name string, short rune, helpvalue ...string) *int { 62 | var p int 63 | s.CounterVarLong(&p, name, short, helpvalue...) 64 | return &p 65 | } 66 | 67 | func CounterVar(p *int, name rune, helpvalue ...string) Option { 68 | return CommandLine.CounterVar(p, name, helpvalue...) 69 | } 70 | 71 | func (s *Set) CounterVar(p *int, name rune, helpvalue ...string) Option { 72 | return s.CounterVarLong(p, "", name, helpvalue...) 73 | } 74 | 75 | func CounterVarLong(p *int, name string, short rune, helpvalue ...string) Option { 76 | return CommandLine.CounterVarLong(p, name, short, helpvalue...) 77 | } 78 | 79 | func (s *Set) CounterVarLong(p *int, name string, short rune, helpvalue ...string) Option { 80 | return s.VarLong((*counterValue)(p), name, short, helpvalue...).SetFlag() 81 | } 82 | -------------------------------------------------------------------------------- /counter_test.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var counterTests = []struct { 14 | where string 15 | in []string 16 | c int 17 | cnt int 18 | err string 19 | }{ 20 | { 21 | loc(), 22 | []string{}, 23 | 0, 24 | 0, 25 | "", 26 | }, 27 | { 28 | loc(), 29 | []string{"test", "-c", "--cnt"}, 30 | 1, 31 | 1, 32 | "", 33 | }, 34 | { 35 | loc(), 36 | []string{"test", "-cc", "-c", "--cnt", "--cnt"}, 37 | 3, 38 | 2, 39 | "", 40 | }, 41 | { 42 | loc(), 43 | []string{"test", "--c=17", "--cnt=42"}, 44 | 17, 45 | 42, 46 | "", 47 | }, 48 | { 49 | loc(), 50 | []string{"test", "--cnt=false"}, 51 | 0, 0, 52 | "test: not a valid number: false\n", 53 | }, 54 | } 55 | 56 | func TestCounter(t *testing.T) { 57 | for x, tt := range counterTests { 58 | reset() 59 | c := Counter('c') 60 | cnt := CounterLong("cnt", 0) 61 | if strings.Index(tt.where, ":-") > 0 { 62 | tt.where = fmt.Sprintf("#%d", x) 63 | } 64 | 65 | parse(tt.in) 66 | if s := checkError(tt.err); s != "" { 67 | t.Errorf("%s: %s", tt.where, s) 68 | } 69 | if got, want := *c, tt.c; got != want { 70 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 71 | } 72 | if got, want := *cnt, tt.cnt; got != want { 73 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 74 | } 75 | } 76 | 77 | reset() 78 | c := 5 79 | opt := CounterVar(&c, 'c') 80 | parse([]string{"test", "-c"}) 81 | if c != 6 { 82 | t.Errorf("got %d, want 6", c) 83 | } 84 | if opt.Count() != 1 { 85 | t.Errorf("got %d, want 1", c) 86 | } 87 | Reset() 88 | if c != 5 { 89 | t.Errorf("got %d, want 5", c) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /duration.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 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 6 | 7 | import "time" 8 | 9 | type durationValue time.Duration 10 | 11 | func (d *durationValue) Set(value string, opt Option) error { 12 | v, err := time.ParseDuration(value) 13 | if err != nil { 14 | return err 15 | } 16 | *d = durationValue(v) 17 | return nil 18 | } 19 | 20 | func (d *durationValue) String() string { 21 | return time.Duration(*d).String() 22 | } 23 | 24 | // Duration creates an option that parses its value as a time.Duration. 25 | func Duration(name rune, value time.Duration, helpvalue ...string) *time.Duration { 26 | return CommandLine.Duration(name, value, helpvalue...) 27 | } 28 | 29 | func (s *Set) Duration(name rune, value time.Duration, helpvalue ...string) *time.Duration { 30 | return s.DurationLong("", name, value, helpvalue...) 31 | } 32 | 33 | func DurationLong(name string, short rune, value time.Duration, helpvalue ...string) *time.Duration { 34 | return CommandLine.DurationLong(name, short, value, helpvalue...) 35 | } 36 | 37 | func (s *Set) DurationLong(name string, short rune, value time.Duration, helpvalue ...string) *time.Duration { 38 | s.DurationVarLong(&value, name, short, helpvalue...) 39 | return &value 40 | } 41 | 42 | func DurationVar(p *time.Duration, name rune, helpvalue ...string) Option { 43 | return CommandLine.DurationVar(p, name, helpvalue...) 44 | } 45 | 46 | func (s *Set) DurationVar(p *time.Duration, name rune, helpvalue ...string) Option { 47 | return s.DurationVarLong(p, "", name, helpvalue...) 48 | } 49 | 50 | func DurationVarLong(p *time.Duration, name string, short rune, helpvalue ...string) Option { 51 | return CommandLine.DurationVarLong(p, name, short, helpvalue...) 52 | } 53 | 54 | func (s *Set) DurationVarLong(p *time.Duration, name string, short rune, helpvalue ...string) Option { 55 | return s.VarLong((*durationValue)(p), name, short, helpvalue...) 56 | } 57 | -------------------------------------------------------------------------------- /duration_test.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | "time" 12 | ) 13 | 14 | var durationTests = []struct { 15 | where string 16 | in []string 17 | d time.Duration 18 | dur time.Duration 19 | err string 20 | }{ 21 | { 22 | loc(), 23 | []string{}, 24 | 17, 42, 25 | "", 26 | }, 27 | { 28 | loc(), 29 | []string{"test", "-d", "1s", "--duration", "2s"}, 30 | time.Second, 2 * time.Second, 31 | "", 32 | }, 33 | { 34 | loc(), 35 | []string{"test", "-d1s", "-d2s"}, 36 | 2 * time.Second, 42, 37 | "", 38 | }, 39 | { 40 | loc(), 41 | []string{"test", "-d1"}, 42 | 17, 42, 43 | "test: time: missing unit in duration", 44 | }, 45 | { 46 | loc(), 47 | []string{"test", "--duration", "foo"}, 48 | 17, 42, 49 | "test: time: invalid duration", 50 | }, 51 | } 52 | 53 | func TestDuration(t *testing.T) { 54 | for x, tt := range durationTests { 55 | reset() 56 | d := Duration('d', 17) 57 | opt := DurationLong("duration", 0, 42) 58 | if strings.Index(tt.where, ":-") > 0 { 59 | tt.where = fmt.Sprintf("#%d", x) 60 | } 61 | 62 | parse(tt.in) 63 | if s := checkError(tt.err); s != "" { 64 | t.Errorf("%s: %s", tt.where, s) 65 | } 66 | if got, want := *d, tt.d; got != want { 67 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 68 | } 69 | if got, want := *opt, tt.dur; got != want { 70 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /enum.go: -------------------------------------------------------------------------------- 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 6 | 7 | import "errors" 8 | 9 | type enumValue string 10 | 11 | var enumValues = make(map[*enumValue]map[string]struct{}) 12 | 13 | func (s *enumValue) Set(value string, opt Option) error { 14 | es, ok := enumValues[s] 15 | if !ok || es == nil { 16 | return errors.New("this option has no values") 17 | } 18 | if _, ok := es[value]; !ok { 19 | return errors.New("invalid value: " + value) 20 | } 21 | *s = enumValue(value) 22 | return nil 23 | } 24 | 25 | func (s *enumValue) String() string { 26 | return string(*s) 27 | } 28 | 29 | // Enum creates an option that can only be set to one of the enumerated strings 30 | // passed in values. Passing nil or an empty slice results in an option that 31 | // will always fail. 32 | func Enum(name rune, values []string, helpvalue ...string) *string { 33 | return CommandLine.Enum(name, values, helpvalue...) 34 | } 35 | 36 | func (s *Set) Enum(name rune, values []string, helpvalue ...string) *string { 37 | var p string 38 | s.EnumVarLong(&p, "", name, values, helpvalue...) 39 | return &p 40 | } 41 | 42 | func EnumLong(name string, short rune, values []string, helpvalue ...string) *string { 43 | return CommandLine.EnumLong(name, short, values, helpvalue...) 44 | } 45 | 46 | func (s *Set) EnumLong(name string, short rune, values []string, helpvalue ...string) *string { 47 | var p string 48 | s.EnumVarLong(&p, name, short, values, helpvalue...) 49 | return &p 50 | } 51 | 52 | // EnumVar creates an enum option that defaults to the starting value of *p. 53 | // If *p is not found in values then a reset of this option will fail. 54 | func EnumVar(p *string, name rune, values []string, helpvalue ...string) Option { 55 | return CommandLine.EnumVar(p, name, values, helpvalue...) 56 | } 57 | 58 | func (s *Set) EnumVar(p *string, name rune, values []string, helpvalue ...string) Option { 59 | return s.EnumVarLong(p, "", name, values, helpvalue...) 60 | } 61 | 62 | func EnumVarLong(p *string, name string, short rune, values []string, helpvalue ...string) Option { 63 | return CommandLine.EnumVarLong(p, name, short, values, helpvalue...) 64 | } 65 | 66 | func (s *Set) EnumVarLong(p *string, name string, short rune, values []string, helpvalue ...string) Option { 67 | m := make(map[string]struct{}) 68 | for _, v := range values { 69 | m[v] = struct{}{} 70 | } 71 | enumValues[(*enumValue)(p)] = m 72 | return s.VarLong((*enumValue)(p), name, short, helpvalue...) 73 | } 74 | -------------------------------------------------------------------------------- /enum_test.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var enumTests = []struct { 14 | where string 15 | in []string 16 | values []string 17 | out string 18 | err string 19 | }{ 20 | { 21 | loc(), 22 | nil, 23 | []string{}, 24 | "", 25 | "", 26 | }, 27 | { 28 | loc(), 29 | []string{"test", "-e", "val1"}, 30 | []string{"val1", "val2"}, 31 | "val1", 32 | "", 33 | }, 34 | { 35 | loc(), 36 | []string{"test", "-e", "val1", "-e", "val2"}, 37 | []string{"val1", "val2"}, 38 | "val2", 39 | "", 40 | }, 41 | { 42 | loc(), 43 | []string{"test", "-e", "val3"}, 44 | []string{"val1", "val2"}, 45 | "", 46 | "test: invalid value: val3\n", 47 | }, 48 | } 49 | 50 | func TestEnum(t *testing.T) { 51 | for x, tt := range enumTests { 52 | if strings.Index(tt.where, ":-") > 0 { 53 | tt.where = fmt.Sprintf("#%d", x) 54 | } 55 | 56 | reset() 57 | e := Enum('e', tt.values) 58 | parse(tt.in) 59 | if s := checkError(tt.err); s != "" { 60 | t.Errorf("%s: %s", tt.where, s) 61 | } 62 | if *e != tt.out { 63 | t.Errorf("%s: got %v, want %v", tt.where, *e, tt.out) 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /error.go: -------------------------------------------------------------------------------- 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 6 | 7 | import "fmt" 8 | 9 | // An Error is returned by Getopt when it encounters an error. 10 | type Error struct { 11 | ErrorCode // General reason of failure. 12 | Err error // The actual error. 13 | Parameter string // Parameter passed to option, if any 14 | Name string // Option that cause error, if any 15 | } 16 | 17 | // Error returns the error message, implementing the error interface. 18 | func (i *Error) Error() string { return i.Err.Error() } 19 | 20 | // An ErrorCode indicates what sort of error was encountered. 21 | type ErrorCode int 22 | 23 | const ( 24 | NoError = ErrorCode(iota) 25 | UnknownOption // an invalid option was encountered 26 | MissingParameter // the options parameter is missing 27 | ExtraParameter // a value was set to a long flag 28 | Invalid // attempt to set an invalid value 29 | ) 30 | 31 | func (e ErrorCode) String() string { 32 | switch e { 33 | case UnknownOption: 34 | return "unknow option" 35 | case MissingParameter: 36 | return "missing argument" 37 | case ExtraParameter: 38 | return "unxpected value" 39 | case Invalid: 40 | return "error setting value" 41 | } 42 | return "unknown error" 43 | } 44 | 45 | // unknownOption returns an Error indicating an unknown option was 46 | // encountered. 47 | func unknownOption(name interface{}) *Error { 48 | i := &Error{ErrorCode: UnknownOption} 49 | switch n := name.(type) { 50 | case rune: 51 | if n == '-' { 52 | i.Name = "-" 53 | } else { 54 | i.Name = "-" + string(n) 55 | } 56 | case string: 57 | i.Name = "--" + n 58 | } 59 | i.Err = fmt.Errorf("unknown option: %s", i.Name) 60 | return i 61 | } 62 | 63 | // missingArg returns an Error inidicating option o was not passed 64 | // a required paramter. 65 | func missingArg(o Option) *Error { 66 | return &Error{ 67 | ErrorCode: MissingParameter, 68 | Name: o.Name(), 69 | Err: fmt.Errorf("missing parameter for %s", o.Name()), 70 | } 71 | } 72 | 73 | // extraArg returns an Error inidicating option o was passed the 74 | // unexpected paramter value. 75 | func extraArg(o Option, value string) *Error { 76 | return &Error{ 77 | ErrorCode: ExtraParameter, 78 | Name: o.Name(), 79 | Parameter: value, 80 | Err: fmt.Errorf("unexpected parameter passed to %s: %q", o.Name(), value), 81 | } 82 | } 83 | 84 | // setError returns an Error inidicating option o and the specified 85 | // error while setting it to value. 86 | func setError(o Option, value string, err error) *Error { 87 | return &Error{ 88 | ErrorCode: Invalid, 89 | Name: o.Name(), 90 | Parameter: value, 91 | Err: err, 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /getopt.go: -------------------------------------------------------------------------------- 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 212 | package getopt 213 | 214 | import ( 215 | "fmt" 216 | "io" 217 | "os" 218 | "path" 219 | "sort" 220 | "strings" 221 | ) 222 | 223 | // stderr allows tests to capture output to standard error. 224 | var stderr io.Writer = os.Stderr 225 | 226 | // exit allows tests to capture an os.Exit call 227 | var exit = os.Exit 228 | 229 | // DisplayWidth is used to determine where to split usage long lines. 230 | var 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 239 | var HelpColumn = 20 240 | 241 | // PrintUsage prints the usage of the program to w. 242 | func (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. 304 | func (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. 346 | func 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. 388 | func (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). 405 | func (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:] 430 | Parsing: 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 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/pborman/getopt 2 | 3 | go 1.13 4 | -------------------------------------------------------------------------------- /int.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | type intValue int 13 | 14 | func (i *intValue) Set(value string, opt Option) error { 15 | v, err := strconv.ParseInt(value, 0, strconv.IntSize) 16 | if err != nil { 17 | if e, ok := err.(*strconv.NumError); ok { 18 | switch e.Err { 19 | case strconv.ErrRange: 20 | err = fmt.Errorf("value out of range: %s", value) 21 | case strconv.ErrSyntax: 22 | err = fmt.Errorf("not a valid number: %s", value) 23 | } 24 | } 25 | return err 26 | } 27 | *i = intValue(v) 28 | return nil 29 | } 30 | 31 | func (i *intValue) String() string { 32 | return strconv.FormatInt(int64(*i), 10) 33 | } 34 | 35 | // Int creates an option that parses its value as an integer. 36 | func Int(name rune, value int, helpvalue ...string) *int { 37 | return CommandLine.Int(name, value, helpvalue...) 38 | } 39 | 40 | func (s *Set) Int(name rune, value int, helpvalue ...string) *int { 41 | return s.IntLong("", name, value, helpvalue...) 42 | } 43 | 44 | func IntLong(name string, short rune, value int, helpvalue ...string) *int { 45 | return CommandLine.IntLong(name, short, value, helpvalue...) 46 | } 47 | 48 | func (s *Set) IntLong(name string, short rune, value int, helpvalue ...string) *int { 49 | s.IntVarLong(&value, name, short, helpvalue...) 50 | return &value 51 | } 52 | 53 | func IntVar(p *int, name rune, helpvalue ...string) Option { 54 | return CommandLine.IntVar(p, name, helpvalue...) 55 | } 56 | 57 | func (s *Set) IntVar(p *int, name rune, helpvalue ...string) Option { 58 | return s.IntVarLong(p, "", name, helpvalue...) 59 | } 60 | 61 | func IntVarLong(p *int, name string, short rune, helpvalue ...string) Option { 62 | return CommandLine.IntVarLong(p, name, short, helpvalue...) 63 | } 64 | 65 | func (s *Set) IntVarLong(p *int, name string, short rune, helpvalue ...string) Option { 66 | return s.VarLong((*intValue)(p), name, short, helpvalue...) 67 | } 68 | -------------------------------------------------------------------------------- /int16.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | type int16Value int16 13 | 14 | func (i *int16Value) Set(value string, opt Option) error { 15 | v, err := strconv.ParseInt(value, 0, 16) 16 | if err != nil { 17 | if e, ok := err.(*strconv.NumError); ok { 18 | switch e.Err { 19 | case strconv.ErrRange: 20 | err = fmt.Errorf("value out of range: %s", value) 21 | case strconv.ErrSyntax: 22 | err = fmt.Errorf("not a valid number: %s", value) 23 | } 24 | } 25 | return err 26 | } 27 | *i = int16Value(v) 28 | return nil 29 | } 30 | 31 | func (i *int16Value) String() string { 32 | return strconv.FormatInt(int64(*i), 10) 33 | } 34 | 35 | // Int16 creates an option that parses its value as an int16. 36 | func Int16(name rune, value int16, helpvalue ...string) *int16 { 37 | return CommandLine.Int16(name, value, helpvalue...) 38 | } 39 | 40 | func (s *Set) Int16(name rune, value int16, helpvalue ...string) *int16 { 41 | return s.Int16Long("", name, value, helpvalue...) 42 | } 43 | 44 | func Int16Long(name string, short rune, value int16, helpvalue ...string) *int16 { 45 | return CommandLine.Int16Long(name, short, value, helpvalue...) 46 | } 47 | 48 | func (s *Set) Int16Long(name string, short rune, value int16, helpvalue ...string) *int16 { 49 | s.Int16VarLong(&value, name, short, helpvalue...) 50 | return &value 51 | } 52 | 53 | func Int16Var(p *int16, name rune, helpvalue ...string) Option { 54 | return CommandLine.Int16Var(p, name, helpvalue...) 55 | } 56 | 57 | func (s *Set) Int16Var(p *int16, name rune, helpvalue ...string) Option { 58 | return s.Int16VarLong(p, "", name, helpvalue...) 59 | } 60 | 61 | func Int16VarLong(p *int16, name string, short rune, helpvalue ...string) Option { 62 | return CommandLine.Int16VarLong(p, name, short, helpvalue...) 63 | } 64 | 65 | func (s *Set) Int16VarLong(p *int16, name string, short rune, helpvalue ...string) Option { 66 | return s.VarLong((*int16Value)(p), name, short, helpvalue...) 67 | } 68 | -------------------------------------------------------------------------------- /int16_test.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var int16Tests = []struct { 14 | where string 15 | in []string 16 | i int16 17 | int16 int16 18 | err string 19 | }{ 20 | { 21 | loc(), 22 | []string{}, 23 | 17, 42, 24 | "", 25 | }, 26 | { 27 | loc(), 28 | []string{"test", "-i", "1", "--int16", "2"}, 29 | 1, 2, 30 | "", 31 | }, 32 | { 33 | loc(), 34 | []string{"test", "-i1", "--int16=2"}, 35 | 1, 2, 36 | "", 37 | }, 38 | { 39 | loc(), 40 | []string{"test", "-i1", "-i2"}, 41 | 2, 42, 42 | "", 43 | }, 44 | { 45 | loc(), 46 | []string{"test", "-i=1"}, 47 | 17, 42, 48 | "test: not a valid number: =1\n", 49 | }, 50 | { 51 | loc(), 52 | []string{"test", "-i0x20"}, 53 | 0x20, 42, 54 | "", 55 | }, 56 | { 57 | loc(), 58 | []string{"test", "-i010"}, 59 | 8, 42, 60 | "", 61 | }, 62 | } 63 | 64 | func TestInt16(t *testing.T) { 65 | for x, tt := range int16Tests { 66 | reset() 67 | i := Int16('i', 17) 68 | opt := Int16Long("int16", 0, 42) 69 | if strings.Index(tt.where, ":-") > 0 { 70 | tt.where = fmt.Sprintf("#%d", x) 71 | } 72 | 73 | parse(tt.in) 74 | if s := checkError(tt.err); s != "" { 75 | t.Errorf("%s: %s", tt.where, s) 76 | } 77 | if got, want := *i, tt.i; got != want { 78 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 79 | } 80 | if got, want := *opt, tt.int16; got != want { 81 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /int32.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | type int32Value int32 13 | 14 | func (i *int32Value) Set(value string, opt Option) error { 15 | v, err := strconv.ParseInt(value, 0, 32) 16 | if err != nil { 17 | if e, ok := err.(*strconv.NumError); ok { 18 | switch e.Err { 19 | case strconv.ErrRange: 20 | err = fmt.Errorf("value out of range: %s", value) 21 | case strconv.ErrSyntax: 22 | err = fmt.Errorf("not a valid number: %s", value) 23 | } 24 | } 25 | return err 26 | } 27 | *i = int32Value(v) 28 | return nil 29 | } 30 | 31 | func (i *int32Value) String() string { 32 | return strconv.FormatInt(int64(*i), 10) 33 | } 34 | 35 | // Int32 creates an option that parses its value as an int32. 36 | func Int32(name rune, value int32, helpvalue ...string) *int32 { 37 | return CommandLine.Int32(name, value, helpvalue...) 38 | } 39 | 40 | func (s *Set) Int32(name rune, value int32, helpvalue ...string) *int32 { 41 | return s.Int32Long("", name, value, helpvalue...) 42 | } 43 | 44 | func Int32Long(name string, short rune, value int32, helpvalue ...string) *int32 { 45 | return CommandLine.Int32Long(name, short, value, helpvalue...) 46 | } 47 | 48 | func (s *Set) Int32Long(name string, short rune, value int32, helpvalue ...string) *int32 { 49 | s.Int32VarLong(&value, name, short, helpvalue...) 50 | return &value 51 | } 52 | 53 | func Int32Var(p *int32, name rune, helpvalue ...string) Option { 54 | return CommandLine.Int32Var(p, name, helpvalue...) 55 | } 56 | 57 | func (s *Set) Int32Var(p *int32, name rune, helpvalue ...string) Option { 58 | return s.Int32VarLong(p, "", name, helpvalue...) 59 | } 60 | 61 | func Int32VarLong(p *int32, name string, short rune, helpvalue ...string) Option { 62 | return CommandLine.Int32VarLong(p, name, short, helpvalue...) 63 | } 64 | 65 | func (s *Set) Int32VarLong(p *int32, name string, short rune, helpvalue ...string) Option { 66 | return s.VarLong((*int32Value)(p), name, short, helpvalue...) 67 | } 68 | -------------------------------------------------------------------------------- /int32_test.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var int32Tests = []struct { 14 | where string 15 | in []string 16 | i int32 17 | int32 int32 18 | err string 19 | }{ 20 | { 21 | loc(), 22 | []string{}, 23 | 17, 42, 24 | "", 25 | }, 26 | { 27 | loc(), 28 | []string{"test", "-i", "1", "--int32", "2"}, 29 | 1, 2, 30 | "", 31 | }, 32 | { 33 | loc(), 34 | []string{"test", "-i1", "--int32=2"}, 35 | 1, 2, 36 | "", 37 | }, 38 | { 39 | loc(), 40 | []string{"test", "-i1", "-i2"}, 41 | 2, 42, 42 | "", 43 | }, 44 | { 45 | loc(), 46 | []string{"test", "-i=1"}, 47 | 17, 42, 48 | "test: not a valid number: =1\n", 49 | }, 50 | { 51 | loc(), 52 | []string{"test", "-i0x20"}, 53 | 0x20, 42, 54 | "", 55 | }, 56 | { 57 | loc(), 58 | []string{"test", "-i010"}, 59 | 8, 42, 60 | "", 61 | }, 62 | } 63 | 64 | func TestInt32(t *testing.T) { 65 | for x, tt := range int32Tests { 66 | reset() 67 | i := Int32('i', 17) 68 | opt := Int32Long("int32", 0, 42) 69 | if strings.Index(tt.where, ":-") > 0 { 70 | tt.where = fmt.Sprintf("#%d", x) 71 | } 72 | 73 | parse(tt.in) 74 | if s := checkError(tt.err); s != "" { 75 | t.Errorf("%s: %s", tt.where, s) 76 | } 77 | if got, want := *i, tt.i; got != want { 78 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 79 | } 80 | if got, want := *opt, tt.int32; got != want { 81 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /int64.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | type int64Value int64 13 | 14 | func (i *int64Value) Set(value string, opt Option) error { 15 | v, err := strconv.ParseInt(value, 0, 64) 16 | if err != nil { 17 | if e, ok := err.(*strconv.NumError); ok { 18 | switch e.Err { 19 | case strconv.ErrRange: 20 | err = fmt.Errorf("value out of range: %s", value) 21 | case strconv.ErrSyntax: 22 | err = fmt.Errorf("not a valid number: %s", value) 23 | } 24 | } 25 | return err 26 | } 27 | *i = int64Value(v) 28 | return nil 29 | } 30 | 31 | func (i *int64Value) String() string { 32 | return strconv.FormatInt(int64(*i), 10) 33 | } 34 | 35 | // Int64 creates an option that parses its value as an int64. 36 | func Int64(name rune, value int64, helpvalue ...string) *int64 { 37 | return CommandLine.Int64(name, value, helpvalue...) 38 | } 39 | 40 | func (s *Set) Int64(name rune, value int64, helpvalue ...string) *int64 { 41 | return s.Int64Long("", name, value, helpvalue...) 42 | } 43 | 44 | func Int64Long(name string, short rune, value int64, helpvalue ...string) *int64 { 45 | return CommandLine.Int64Long(name, short, value, helpvalue...) 46 | } 47 | 48 | func (s *Set) Int64Long(name string, short rune, value int64, helpvalue ...string) *int64 { 49 | s.Int64VarLong(&value, name, short, helpvalue...) 50 | return &value 51 | } 52 | 53 | func Int64Var(p *int64, name rune, helpvalue ...string) Option { 54 | return CommandLine.Int64Var(p, name, helpvalue...) 55 | } 56 | 57 | func (s *Set) Int64Var(p *int64, name rune, helpvalue ...string) Option { 58 | return s.Int64VarLong(p, "", name, helpvalue...) 59 | } 60 | 61 | func Int64VarLong(p *int64, name string, short rune, helpvalue ...string) Option { 62 | return CommandLine.Int64VarLong(p, name, short, helpvalue...) 63 | } 64 | 65 | func (s *Set) Int64VarLong(p *int64, name string, short rune, helpvalue ...string) Option { 66 | return s.VarLong((*int64Value)(p), name, short, helpvalue...) 67 | } 68 | -------------------------------------------------------------------------------- /int64_test.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var int64Tests = []struct { 14 | where string 15 | in []string 16 | i int64 17 | int64 int64 18 | err string 19 | }{ 20 | { 21 | loc(), 22 | []string{}, 23 | 17, 42, 24 | "", 25 | }, 26 | { 27 | loc(), 28 | []string{"test", "-i", "1", "--int64", "2"}, 29 | 1, 2, 30 | "", 31 | }, 32 | { 33 | loc(), 34 | []string{"test", "-i1", "--int64=2"}, 35 | 1, 2, 36 | "", 37 | }, 38 | { 39 | loc(), 40 | []string{"test", "-i1", "-i2"}, 41 | 2, 42, 42 | "", 43 | }, 44 | { 45 | loc(), 46 | []string{"test", "-i=1"}, 47 | 17, 42, 48 | "test: not a valid number: =1\n", 49 | }, 50 | { 51 | loc(), 52 | []string{"test", "-i0x20"}, 53 | 0x20, 42, 54 | "", 55 | }, 56 | { 57 | loc(), 58 | []string{"test", "-i010"}, 59 | 8, 42, 60 | "", 61 | }, 62 | } 63 | 64 | func TestInt64(t *testing.T) { 65 | for x, tt := range int64Tests { 66 | reset() 67 | i := Int64('i', 17) 68 | opt := Int64Long("int64", 0, 42) 69 | if strings.Index(tt.where, ":-") > 0 { 70 | tt.where = fmt.Sprintf("#%d", x) 71 | } 72 | 73 | parse(tt.in) 74 | if s := checkError(tt.err); s != "" { 75 | t.Errorf("%s: %s", tt.where, s) 76 | } 77 | if got, want := *i, tt.i; got != want { 78 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 79 | } 80 | if got, want := *opt, tt.int64; got != want { 81 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /int_test.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var intTests = []struct { 14 | where string 15 | in []string 16 | i int 17 | int int 18 | err string 19 | }{ 20 | { 21 | loc(), 22 | []string{}, 23 | 17, 42, 24 | "", 25 | }, 26 | { 27 | loc(), 28 | []string{"test", "-i", "1", "--int", "2"}, 29 | 1, 2, 30 | "", 31 | }, 32 | { 33 | loc(), 34 | []string{"test", "-i1", "--int=2"}, 35 | 1, 2, 36 | "", 37 | }, 38 | { 39 | loc(), 40 | []string{"test", "-i1", "-i2"}, 41 | 2, 42, 42 | "", 43 | }, 44 | { 45 | loc(), 46 | []string{"test", "-i=1"}, 47 | 17, 42, 48 | "test: not a valid number: =1\n", 49 | }, 50 | { 51 | loc(), 52 | []string{"test", "-i0x20"}, 53 | 0x20, 42, 54 | "", 55 | }, 56 | { 57 | loc(), 58 | []string{"test", "-i010"}, 59 | 8, 42, 60 | "", 61 | }, 62 | } 63 | 64 | func TestInt(t *testing.T) { 65 | for x, tt := range intTests { 66 | reset() 67 | i := Int('i', 17) 68 | opt := IntLong("int", 0, 42) 69 | if strings.Index(tt.where, ":-") > 0 { 70 | tt.where = fmt.Sprintf("#%d", x) 71 | } 72 | 73 | parse(tt.in) 74 | if s := checkError(tt.err); s != "" { 75 | t.Errorf("%s: %s", tt.where, s) 76 | } 77 | if got, want := *i, tt.i; got != want { 78 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 79 | } 80 | if got, want := *opt, tt.int; got != want { 81 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /list.go: -------------------------------------------------------------------------------- 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 6 | 7 | import "strings" 8 | 9 | type listValue []string 10 | 11 | func (s *listValue) Set(value string, opt Option) error { 12 | a := strings.Split(value, ",") 13 | // If this is the first time we are seen then nil out the 14 | // default value. 15 | if opt.Count() <= 1 { 16 | *s = nil 17 | } 18 | *s = append(*s, a...) 19 | return nil 20 | } 21 | 22 | func (s *listValue) String() string { 23 | return strings.Join([]string(*s), ",") 24 | } 25 | 26 | // List creates an option that returns a slice of strings. The parameters 27 | // passed are converted from a comma seperated value list into a slice. 28 | // Subsequent occurrences append to the list. 29 | func List(name rune, helpvalue ...string) *[]string { 30 | return CommandLine.List(name, helpvalue...) 31 | } 32 | 33 | func (s *Set) List(name rune, helpvalue ...string) *[]string { 34 | p := []string{} 35 | s.ListVar(&p, name, helpvalue...) 36 | return &p 37 | } 38 | 39 | func ListLong(name string, short rune, helpvalue ...string) *[]string { 40 | return CommandLine.ListLong(name, short, helpvalue...) 41 | } 42 | 43 | func (s *Set) ListLong(name string, short rune, helpvalue ...string) *[]string { 44 | p := []string{} 45 | s.ListVarLong(&p, name, short, helpvalue...) 46 | return &p 47 | } 48 | 49 | // ListVar creats a list option and places the values in p. If p is pointing 50 | // to a list of values then those are considered the default values. The first 51 | // time name is seen in the options the list will be set to list specified by 52 | // the parameter to the option. Subsequent instances of the option will append 53 | // to the list. 54 | func ListVar(p *[]string, name rune, helpvalue ...string) Option { 55 | return CommandLine.ListVar(p, name, helpvalue...) 56 | } 57 | 58 | func (s *Set) ListVar(p *[]string, name rune, helpvalue ...string) Option { 59 | return s.ListVarLong(p, "", name, helpvalue...) 60 | } 61 | 62 | func ListVarLong(p *[]string, name string, short rune, helpvalue ...string) Option { 63 | return CommandLine.ListVarLong(p, name, short, helpvalue...) 64 | } 65 | 66 | func (s *Set) ListVarLong(p *[]string, name string, short rune, helpvalue ...string) Option { 67 | opt := s.VarLong((*listValue)(p), name, short, helpvalue...) 68 | return opt 69 | } 70 | -------------------------------------------------------------------------------- /list_test.go: -------------------------------------------------------------------------------- 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 6 | 7 | import "testing" 8 | 9 | var listTests = []struct { 10 | where string 11 | in []string 12 | l, list []string 13 | err string 14 | }{ 15 | { 16 | loc(), 17 | []string{}, 18 | nil, nil, 19 | "", 20 | }, 21 | { 22 | loc(), 23 | []string{"test", "-l", "one"}, 24 | []string{"one"}, nil, 25 | "", 26 | }, 27 | { 28 | loc(), 29 | []string{"test", "-lone", "-ltwo"}, 30 | []string{"one", "two"}, nil, 31 | "", 32 | }, 33 | { 34 | loc(), 35 | []string{"test", "--list", "one"}, 36 | nil, []string{"one"}, 37 | "", 38 | }, 39 | { 40 | loc(), 41 | []string{"test", "--list=one", "--list=two"}, 42 | nil, []string{"one", "two"}, 43 | "", 44 | }, 45 | { 46 | loc(), 47 | []string{"test", "--list=one,two"}, 48 | nil, []string{"one", "two"}, 49 | "", 50 | }, 51 | } 52 | 53 | func TestList(t *testing.T) { 54 | for _, tt := range listTests { 55 | reset() 56 | l := List('l') 57 | list := ListLong("list", 0) 58 | 59 | parse(tt.in) 60 | if s := checkError(tt.err); s != "" { 61 | t.Errorf("%s: %s", tt.where, s) 62 | } 63 | if badSlice(*l, tt.l) { 64 | t.Errorf("%s: got s = %q, want %q", tt.where, *l, tt.l) 65 | } 66 | if badSlice(*list, tt.list) { 67 | t.Errorf("%s: got s = %q, want %q", tt.where, *list, tt.list) 68 | } 69 | } 70 | } 71 | 72 | func TestDefaultList(t *testing.T) { 73 | reset() 74 | list := []string{"d1", "d2", "d3"} 75 | ListVar(&list, 'l') 76 | parse([]string{"test"}) 77 | 78 | want := []string{"d1", "d2", "d3"} 79 | if badSlice(list, want) { 80 | t.Errorf("got s = %q, want %q", list, want) 81 | } 82 | 83 | parse([]string{"test", "-l", "one"}) 84 | want = []string{"one"} 85 | if badSlice(list, want) { 86 | t.Errorf("got s = %q, want %q", list, want) 87 | } 88 | 89 | parse([]string{"test", "-l", "two"}) 90 | want = []string{"one", "two"} 91 | if badSlice(list, want) { 92 | t.Errorf("got s = %q, want %q", list, want) 93 | } 94 | Lookup('l').Reset() 95 | want = []string{"d1", "d2", "d3"} 96 | if badSlice(list, want) { 97 | t.Errorf("got s = %q, want %q", list, want) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /option.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | ) 11 | 12 | // An Option can be either a Flag or a Value 13 | type 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 | // insenstive, 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 | 56 | type 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 80 | func (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. 109 | func (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 | 116 | func (o *option) Seen() bool { return o.count > 0 } 117 | func (o *option) Count() int { return o.count } 118 | func (o *option) IsFlag() bool { return o.flag } 119 | func (o *option) String() string { return o.value.String() } 120 | func (o *option) SetOptional() Option { o.optional = true; return o } 121 | func (o *option) SetFlag() Option { o.flag = true; return o } 122 | 123 | func (o *option) Value() Value { 124 | if o == nil { 125 | return nil 126 | } 127 | return o.value 128 | } 129 | 130 | func (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. 138 | func (o *option) Reset() { 139 | o.isLong = false 140 | o.count = 0 141 | o.value.Set(o.defval, o) 142 | } 143 | 144 | type optionList []*option 145 | 146 | func (ol optionList) Len() int { return len(ol) } 147 | func (ol optionList) Swap(i, j int) { ol[i], ol[j] = ol[j], ol[i] } 148 | func (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. 166 | func 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. 171 | func (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 | -------------------------------------------------------------------------------- /set.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "io" 9 | "os" 10 | "sort" 11 | ) 12 | 13 | // A Termination says why Getopt returned. 14 | type State int 15 | 16 | const ( 17 | InProgress = State(iota) // Getopt is still running 18 | Dash // Returned on "-" 19 | DashDash // Returned on "--" 20 | EndOfOptions // End of options reached 21 | EndOfArguments // No more arguments 22 | Terminated // Terminated by callback function 23 | Failure // Terminated due to error 24 | Unknown // Indicates internal error 25 | ) 26 | 27 | type Set struct { 28 | State // State of getopt 29 | 30 | // args are the parameters remaining after parsing the optoins. 31 | args []string 32 | 33 | // program is the name of the program for usage and error messages. 34 | // If not set it will automatically be set to the base name of the 35 | // first argument passed to parse. 36 | program string 37 | 38 | // parameters is what is displayed on the usage line after displaying 39 | // the various options. 40 | parameters string 41 | 42 | usage func() // usage should print the programs usage and exit. 43 | 44 | shortOptions map[rune]*option 45 | longOptions map[string]*option 46 | options optionList 47 | } 48 | 49 | // New returns a newly created option set. 50 | func New() *Set { 51 | s := &Set{ 52 | shortOptions: make(map[rune]*option), 53 | longOptions: make(map[string]*option), 54 | parameters: "[parameters ...]", 55 | } 56 | 57 | s.usage = func() { 58 | s.PrintUsage(stderr) 59 | } 60 | return s 61 | } 62 | 63 | // The default set of command-line options. 64 | var CommandLine = New() 65 | 66 | // PrintUsage calls PrintUsage in the default option set. 67 | func PrintUsage(w io.Writer) { CommandLine.PrintUsage(w) } 68 | 69 | // Usage calls the usage function in the default option set. 70 | func Usage() { CommandLine.usage() } 71 | 72 | // Parse calls Parse in the default option set with the command line arguments 73 | // found in os.Args. 74 | func Parse() { CommandLine.Parse(os.Args) } 75 | 76 | // Getops returns the result of calling Getop in the default option set with the 77 | // command line arguments found in os.Args. The fn function, which may be nil, 78 | // is passed to Getopt. 79 | func Getopt(fn func(Option) bool) error { return CommandLine.Getopt(os.Args, fn) } 80 | 81 | // Arg returns the n'th command-line argument. Arg(0) is the first remaining 82 | // argument after options have been processed. 83 | func Arg(n int) string { 84 | if n >= 0 && n < len(CommandLine.args) { 85 | return CommandLine.args[n] 86 | } 87 | return "" 88 | } 89 | 90 | // Arg returns the n'th argument. Arg(0) is the first remaining 91 | // argument after options have been processed. 92 | func (s *Set) Arg(n int) string { 93 | if n >= 0 && n < len(s.args) { 94 | return s.args[n] 95 | } 96 | return "" 97 | } 98 | 99 | // Args returns the non-option command line arguments. 100 | func Args() []string { 101 | return CommandLine.args 102 | } 103 | 104 | // Args returns the non-option arguments. 105 | func (s *Set) Args() []string { 106 | return s.args 107 | } 108 | 109 | // NArgs returns the number of non-option command line arguments. 110 | func NArgs() int { 111 | return len(CommandLine.args) 112 | } 113 | 114 | // NArgs returns the number of non-option arguments. 115 | func (s *Set) NArgs() int { 116 | return len(s.args) 117 | } 118 | 119 | // SetParameters sets the parameters string for printing the command line 120 | // usage. It defaults to "[parameters ...]" 121 | func SetParameters(parameters string) { 122 | CommandLine.parameters = parameters 123 | } 124 | 125 | // SetParameters sets the parameters string for printing the s's usage. 126 | // It defaults to "[parameters ...]" 127 | func (s *Set) SetParameters(parameters string) { 128 | s.parameters = parameters 129 | } 130 | 131 | // SetProgram sets the program name to program. Nomrally it is determined 132 | // from the zeroth command line argument (see os.Args). 133 | func SetProgram(program string) { 134 | CommandLine.program = program 135 | } 136 | 137 | // SetProgram sets s's program name to program. Nomrally it is determined 138 | // from the zeroth argument passed to Getopt or Parse. 139 | func (s *Set) SetProgram(program string) { 140 | s.program = program 141 | } 142 | 143 | // SetUsage sets the function used by Parse to display the commands usage 144 | // on error. It defaults to calling PrintUsage(os.Stderr). 145 | func SetUsage(usage func()) { 146 | CommandLine.usage = usage 147 | } 148 | 149 | // SetUsage sets the function used by Parse to display usage on error. It 150 | // defaults to calling f.PrintUsage(os.Stderr). 151 | func (s *Set) SetUsage(usage func()) { 152 | s.usage = usage 153 | } 154 | 155 | // Lookup returns the Option associated with name. Name should either be 156 | // a rune (the short name) or a string (the long name). 157 | func Lookup(name interface{}) Option { 158 | return CommandLine.Lookup(name) 159 | } 160 | 161 | // Lookup returns the Option associated with name in s. Name should either be 162 | // a rune (the short name) or a string (the long name). 163 | func (s *Set) Lookup(name interface{}) Option { 164 | switch v := name.(type) { 165 | case rune: 166 | return s.shortOptions[v] 167 | case int: 168 | return s.shortOptions[rune(v)] 169 | case string: 170 | return s.longOptions[v] 171 | } 172 | return nil 173 | } 174 | 175 | // IsSet returns true if the Option associated with name was seen while 176 | // parsing the command line arguments. Name should either be a rune (the 177 | // short name) or a string (the long name). 178 | func IsSet(name interface{}) bool { 179 | return CommandLine.IsSet(name) 180 | } 181 | 182 | // IsSet returns true if the Option associated with name was seen while 183 | // parsing s. Name should either be a rune (the short name) or a string (the 184 | // long name). 185 | func (s *Set) IsSet(name interface{}) bool { 186 | if opt := s.Lookup(name); opt != nil { 187 | return opt.Seen() 188 | } 189 | return false 190 | } 191 | 192 | // GetCount returns the number of times the Option associated with name has been 193 | // seen while parsing the command line arguments. Name should either be a rune 194 | // (the short name) or a string (the long name). 195 | func GetCount(name interface{}) int { 196 | return CommandLine.GetCount(name) 197 | } 198 | 199 | // GetCount returns the number of times the Option associated with name has been 200 | // seen while parsing s's arguments. Name should either be a rune (the short 201 | // name) or a string (the long name). 202 | func (s *Set) GetCount(name interface{}) int { 203 | if opt := s.Lookup(name); opt != nil { 204 | return opt.Count() 205 | } 206 | return 0 207 | } 208 | 209 | // GetValue returns the final value set to the command-line Option with name. 210 | // If the option has not been seen while parsing s then the default value is 211 | // returned. Name should either be a rune (the short name) or a string (the 212 | // long name). 213 | func GetValue(name interface{}) string { 214 | return CommandLine.GetValue(name) 215 | } 216 | 217 | // GetValue returns the final value set to the Option in s associated with name. 218 | // If the option has not been seen while parsing s then the default value is 219 | // returned. Name should either be a rune (the short name) or a string (the 220 | // long name). 221 | func (s *Set) GetValue(name interface{}) string { 222 | if opt := s.Lookup(name); opt != nil { 223 | return opt.String() 224 | } 225 | return "" 226 | } 227 | 228 | // Visit visits the command-line options in lexicographical order, calling fn 229 | // for each. It visits only those options that have been set. 230 | func Visit(fn func(Option)) { CommandLine.Visit(fn) } 231 | 232 | // Visit visits the options in s in lexicographical order, calling fn 233 | // for each. It visits only those options that have been set. 234 | func (s *Set) Visit(fn func(Option)) { 235 | sort.Sort(s.options) 236 | for _, opt := range s.options { 237 | if opt.count > 0 { 238 | fn(opt) 239 | } 240 | } 241 | } 242 | 243 | // VisitAll visits the options in s in lexicographical order, calling fn 244 | // for each. It visits all options, even those not set. 245 | func VisitAll(fn func(Option)) { CommandLine.VisitAll(fn) } 246 | 247 | // VisitAll visits the command-line flags in lexicographical order, calling fn 248 | // for each. It visits all flags, even those not set. 249 | func (s *Set) VisitAll(fn func(Option)) { 250 | sort.Sort(s.options) 251 | for _, opt := range s.options { 252 | fn(opt) 253 | } 254 | } 255 | 256 | // Reset resets all the command line options to the initial state so it 257 | // appears none of them have been seen. 258 | func Reset() { 259 | CommandLine.Reset() 260 | } 261 | 262 | // Reset resets all the options in s to the initial state so it 263 | // appears none of them have been seen. 264 | func (s *Set) Reset() { 265 | for _, opt := range s.options { 266 | opt.Reset() 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /signed.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | type signed int64 13 | 14 | type SignedLimit struct { 15 | Base int // Base for conversion as per strconv.ParseInt 16 | Bits int // Number of bits as per strconv.ParseInt 17 | Min int64 // Minimum allowed value if both Min and Max are not 0 18 | Max int64 // Maximum allowed value if both Min and Max are not 0 19 | } 20 | 21 | var signedLimits = make(map[*signed]*SignedLimit) 22 | 23 | func (n *signed) Set(value string, opt Option) error { 24 | l := signedLimits[n] 25 | if l == nil { 26 | return fmt.Errorf("no limits defined for %s", opt.Name()) 27 | } 28 | v, err := strconv.ParseInt(value, l.Base, l.Bits) 29 | if err != nil { 30 | if e, ok := err.(*strconv.NumError); ok { 31 | switch e.Err { 32 | case strconv.ErrRange: 33 | err = fmt.Errorf("value out of range: %s", value) 34 | case strconv.ErrSyntax: 35 | err = fmt.Errorf("not a valid number: %s", value) 36 | } 37 | } 38 | return err 39 | } 40 | if l.Min != 0 || l.Max != 0 { 41 | if v < l.Min { 42 | return fmt.Errorf("value out of range (<%v): %s", l.Min, value) 43 | } 44 | if v > l.Max { 45 | return fmt.Errorf("value out of range (>%v): %s", l.Max, value) 46 | } 47 | } 48 | *n = signed(v) 49 | return nil 50 | } 51 | 52 | func (n *signed) String() string { 53 | l := signedLimits[n] 54 | if l != nil && l.Base != 0 { 55 | return strconv.FormatInt(int64(*n), l.Base) 56 | } 57 | return strconv.FormatInt(int64(*n), 10) 58 | } 59 | 60 | // Signed creates an option that is stored in an int64 and is constrained 61 | // by the limits pointed to by l. The Max and Min values are only used if 62 | // at least one of the values are not 0. If Base is 0, the base is implied by 63 | // the string's prefix: base 16 for "0x", base 8 for "0", and base 10 otherwise. 64 | func Signed(name rune, value int64, l *SignedLimit, helpvalue ...string) *int64 { 65 | return CommandLine.Signed(name, value, l, helpvalue...) 66 | } 67 | 68 | func (s *Set) Signed(name rune, value int64, l *SignedLimit, helpvalue ...string) *int64 { 69 | return s.SignedLong("", name, value, l, helpvalue...) 70 | } 71 | 72 | func SignedLong(name string, short rune, value int64, l *SignedLimit, helpvalue ...string) *int64 { 73 | return CommandLine.SignedLong(name, short, value, l, helpvalue...) 74 | } 75 | 76 | func (s *Set) SignedLong(name string, short rune, value int64, l *SignedLimit, helpvalue ...string) *int64 { 77 | s.SignedVarLong(&value, name, short, l, helpvalue...) 78 | return &value 79 | } 80 | 81 | func SignedVar(p *int64, name rune, l *SignedLimit, helpvalue ...string) Option { 82 | return CommandLine.SignedVar(p, name, l, helpvalue...) 83 | } 84 | 85 | func (s *Set) SignedVar(p *int64, name rune, l *SignedLimit, helpvalue ...string) Option { 86 | return s.SignedVarLong(p, "", name, l, helpvalue...) 87 | } 88 | 89 | func SignedVarLong(p *int64, name string, short rune, l *SignedLimit, helpvalue ...string) Option { 90 | return CommandLine.SignedVarLong(p, name, short, l, helpvalue...) 91 | } 92 | 93 | func (s *Set) SignedVarLong(p *int64, name string, short rune, l *SignedLimit, helpvalue ...string) Option { 94 | opt := s.VarLong((*signed)(p), name, short, helpvalue...) 95 | if l.Base > 36 || l.Base == 1 || l.Base < 0 { 96 | fmt.Fprintf(stderr, "invalid base for %s: %d\n", opt.Name(), l.Base) 97 | exit(1) 98 | } 99 | if l.Bits < 0 || l.Bits > 64 { 100 | fmt.Fprintf(stderr, "invalid bit size for %s: %d\n", opt.Name(), l.Bits) 101 | exit(1) 102 | } 103 | if l.Min > l.Max { 104 | fmt.Fprintf(stderr, "min greater than max for %s\n", opt.Name()) 105 | exit(1) 106 | } 107 | lim := *l 108 | signedLimits[(*signed)(p)] = &lim 109 | return opt 110 | } 111 | -------------------------------------------------------------------------------- /signed_test.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var signedNumberTests = []struct { 14 | where string 15 | in []string 16 | l SignedLimit 17 | out int64 18 | err string 19 | }{ 20 | { 21 | where: loc(), 22 | }, 23 | { 24 | loc(), 25 | []string{"test", "-n", "1010"}, 26 | SignedLimit{Base: 2, Bits: 5}, 27 | 10, 28 | "", 29 | }, 30 | { 31 | loc(), 32 | []string{"test", "-n", "1010"}, 33 | SignedLimit{Base: 2, Bits: 4}, 34 | 0, 35 | "test: value out of range: 1010\n", 36 | }, 37 | { 38 | loc(), 39 | []string{"test", "-n", "-1000"}, 40 | SignedLimit{Base: 2, Bits: 4}, 41 | -8, 42 | "", 43 | }, 44 | { 45 | loc(), 46 | []string{"test", "-n", "3"}, 47 | SignedLimit{Min: 4, Max: 6}, 48 | 0, 49 | "test: value out of range (<4): 3\n", 50 | }, 51 | { 52 | loc(), 53 | []string{"test", "-n", "4"}, 54 | SignedLimit{Min: 4, Max: 6}, 55 | 4, 56 | "", 57 | }, 58 | { 59 | loc(), 60 | []string{"test", "-n", "5"}, 61 | SignedLimit{Min: 4, Max: 6}, 62 | 5, 63 | "", 64 | }, 65 | { 66 | loc(), 67 | []string{"test", "-n", "6"}, 68 | SignedLimit{Min: 4, Max: 6}, 69 | 6, 70 | "", 71 | }, 72 | { 73 | loc(), 74 | []string{"test", "-n", "7"}, 75 | SignedLimit{Min: 4, Max: 6}, 76 | 0, 77 | "test: value out of range (>6): 7\n", 78 | }, 79 | } 80 | 81 | func TestSigneds(t *testing.T) { 82 | for x, tt := range signedNumberTests { 83 | if strings.Index(tt.where, ":-") > 0 { 84 | tt.where = fmt.Sprintf("#%d", x) 85 | } 86 | 87 | reset() 88 | n := Signed('n', 0, &tt.l) 89 | parse(tt.in) 90 | if s := checkError(tt.err); s != "" { 91 | t.Errorf("%s: %s", tt.where, s) 92 | } 93 | if *n != tt.out { 94 | t.Errorf("%s: got %v, want %v", tt.where, *n, tt.out) 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /string.go: -------------------------------------------------------------------------------- 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 6 | 7 | type stringValue string 8 | 9 | func (s *stringValue) Set(value string, opt Option) error { 10 | *s = stringValue(value) 11 | return nil 12 | } 13 | 14 | func (s *stringValue) String() string { 15 | return string(*s) 16 | } 17 | 18 | // String returns a value option that stores is value as a string. The 19 | // initial value of the string is passed in value. 20 | func String(name rune, value string, helpvalue ...string) *string { 21 | return CommandLine.String(name, value, helpvalue...) 22 | } 23 | 24 | func (s *Set) String(name rune, value string, helpvalue ...string) *string { 25 | p := value 26 | s.StringVarLong(&p, "", name, helpvalue...) 27 | return &p 28 | } 29 | 30 | func StringLong(name string, short rune, value string, helpvalue ...string) *string { 31 | return CommandLine.StringLong(name, short, value, helpvalue...) 32 | } 33 | 34 | func (s *Set) StringLong(name string, short rune, value string, helpvalue ...string) *string { 35 | s.StringVarLong(&value, name, short, helpvalue...) 36 | return &value 37 | } 38 | 39 | func StringVar(p *string, name rune, helpvalue ...string) Option { 40 | return CommandLine.StringVar(p, name, helpvalue...) 41 | } 42 | 43 | func (s *Set) StringVar(p *string, name rune, helpvalue ...string) Option { 44 | return s.VarLong((*stringValue)(p), "", name, helpvalue...) 45 | } 46 | 47 | func StringVarLong(p *string, name string, short rune, helpvalue ...string) Option { 48 | return CommandLine.StringVarLong(p, name, short, helpvalue...) 49 | } 50 | 51 | func (s *Set) StringVarLong(p *string, name string, short rune, helpvalue ...string) Option { 52 | return s.VarLong((*stringValue)(p), name, short, helpvalue...) 53 | } 54 | -------------------------------------------------------------------------------- /string_test.go: -------------------------------------------------------------------------------- 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 6 | 7 | import "testing" 8 | 9 | var stringTests = []struct { 10 | where string 11 | in []string 12 | sout string 13 | optout string 14 | err string 15 | }{ 16 | { 17 | loc(), 18 | []string{}, 19 | "one", 20 | "two", 21 | "", 22 | }, 23 | { 24 | loc(), 25 | []string{"test", "-s"}, 26 | "one", 27 | "two", 28 | "test: missing parameter for -s\n", 29 | }, 30 | { 31 | loc(), 32 | []string{"test", "--opt"}, 33 | "one", 34 | "two", 35 | "test: missing parameter for --opt\n", 36 | }, 37 | { 38 | loc(), 39 | []string{"test", "-svalue", "--opt=option"}, 40 | "value", 41 | "option", 42 | "", 43 | }, 44 | { 45 | loc(), 46 | []string{"test", "-s", "value", "--opt", "option"}, 47 | "value", 48 | "option", 49 | "", 50 | }, 51 | { 52 | loc(), 53 | []string{"test", "-swrong", "--opt=wrong", "-s", "value", "--opt", "option"}, 54 | "value", 55 | "option", 56 | "", 57 | }, 58 | } 59 | 60 | func TestString(t *testing.T) { 61 | for _, tt := range stringTests { 62 | reset() 63 | s := String('s', "one") 64 | opt := StringLong("opt", 0, "two") 65 | 66 | parse(tt.in) 67 | if s := checkError(tt.err); s != "" { 68 | t.Errorf("%s: %s", tt.where, s) 69 | } 70 | if *s != tt.sout { 71 | t.Errorf("%s: got s = %q, want %q", tt.where, *s, tt.sout) 72 | } 73 | if *opt != tt.optout { 74 | t.Errorf("%s: got opt = %q, want %q", tt.where, *opt, tt.optout) 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /uint.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | type uintValue uint 13 | 14 | func (i *uintValue) Set(value string, opt Option) error { 15 | v, err := strconv.ParseUint(value, 0, strconv.IntSize) 16 | if err != nil { 17 | if e, ok := err.(*strconv.NumError); ok { 18 | switch e.Err { 19 | case strconv.ErrRange: 20 | err = fmt.Errorf("value out of range: %s", value) 21 | case strconv.ErrSyntax: 22 | err = fmt.Errorf("not a valid number: %s", value) 23 | } 24 | } 25 | return err 26 | } 27 | *i = uintValue(v) 28 | return nil 29 | } 30 | 31 | func (i *uintValue) String() string { 32 | return strconv.FormatUint(uint64(*i), 10) 33 | } 34 | 35 | // Uint creates an option that parses its value as an unsigned integer. 36 | func Uint(name rune, value uint, helpvalue ...string) *uint { 37 | return CommandLine.Uint(name, value, helpvalue...) 38 | } 39 | 40 | func (s *Set) Uint(name rune, value uint, helpvalue ...string) *uint { 41 | return s.UintLong("", name, value, helpvalue...) 42 | } 43 | 44 | func UintLong(name string, short rune, value uint, helpvalue ...string) *uint { 45 | return CommandLine.UintLong(name, short, value, helpvalue...) 46 | } 47 | 48 | func (s *Set) UintLong(name string, short rune, value uint, helpvalue ...string) *uint { 49 | s.UintVarLong(&value, name, short, helpvalue...) 50 | return &value 51 | } 52 | 53 | func UintVar(p *uint, name rune, helpvalue ...string) Option { 54 | return CommandLine.UintVar(p, name, helpvalue...) 55 | } 56 | 57 | func (s *Set) UintVar(p *uint, name rune, helpvalue ...string) Option { 58 | return s.UintVarLong(p, "", name, helpvalue...) 59 | } 60 | 61 | func UintVarLong(p *uint, name string, short rune, helpvalue ...string) Option { 62 | return CommandLine.UintVarLong(p, name, short, helpvalue...) 63 | } 64 | 65 | func (s *Set) UintVarLong(p *uint, name string, short rune, helpvalue ...string) Option { 66 | return s.VarLong((*uintValue)(p), name, short, helpvalue...) 67 | } 68 | -------------------------------------------------------------------------------- /uint16.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | type uint16Value uint16 13 | 14 | func (i *uint16Value) Set(value string, opt Option) error { 15 | v, err := strconv.ParseUint(value, 0, 16) 16 | if err != nil { 17 | if e, ok := err.(*strconv.NumError); ok { 18 | switch e.Err { 19 | case strconv.ErrRange: 20 | err = fmt.Errorf("value out of range: %s", value) 21 | case strconv.ErrSyntax: 22 | err = fmt.Errorf("not a valid number: %s", value) 23 | } 24 | } 25 | return err 26 | } 27 | *i = uint16Value(v) 28 | return nil 29 | } 30 | 31 | func (i *uint16Value) String() string { 32 | return strconv.FormatUint(uint64(*i), 10) 33 | } 34 | 35 | // Uint16 creates an option that parses its value as an uint16. 36 | func Uint16(name rune, value uint16, helpvalue ...string) *uint16 { 37 | return CommandLine.Uint16(name, value, helpvalue...) 38 | } 39 | 40 | func (s *Set) Uint16(name rune, value uint16, helpvalue ...string) *uint16 { 41 | return s.Uint16Long("", name, value, helpvalue...) 42 | } 43 | 44 | func Uint16Long(name string, short rune, value uint16, helpvalue ...string) *uint16 { 45 | return CommandLine.Uint16Long(name, short, value, helpvalue...) 46 | } 47 | 48 | func (s *Set) Uint16Long(name string, short rune, value uint16, helpvalue ...string) *uint16 { 49 | s.Uint16VarLong(&value, name, short, helpvalue...) 50 | return &value 51 | } 52 | 53 | func Uint16Var(p *uint16, name rune, helpvalue ...string) Option { 54 | return CommandLine.Uint16Var(p, name, helpvalue...) 55 | } 56 | 57 | func (s *Set) Uint16Var(p *uint16, name rune, helpvalue ...string) Option { 58 | return s.Uint16VarLong(p, "", name, helpvalue...) 59 | } 60 | 61 | func Uint16VarLong(p *uint16, name string, short rune, helpvalue ...string) Option { 62 | return CommandLine.Uint16VarLong(p, name, short, helpvalue...) 63 | } 64 | 65 | func (s *Set) Uint16VarLong(p *uint16, name string, short rune, helpvalue ...string) Option { 66 | return s.VarLong((*uint16Value)(p), name, short, helpvalue...) 67 | } 68 | -------------------------------------------------------------------------------- /uint16_test.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var uint16Tests = []struct { 14 | where string 15 | in []string 16 | i uint16 17 | uint16 uint16 18 | err string 19 | }{ 20 | { 21 | loc(), 22 | []string{}, 23 | 17, 42, 24 | "", 25 | }, 26 | { 27 | loc(), 28 | []string{"test", "-i", "1", "--uint16", "2"}, 29 | 1, 2, 30 | "", 31 | }, 32 | { 33 | loc(), 34 | []string{"test", "-i1", "--uint16=2"}, 35 | 1, 2, 36 | "", 37 | }, 38 | { 39 | loc(), 40 | []string{"test", "-i1", "-i2"}, 41 | 2, 42, 42 | "", 43 | }, 44 | { 45 | loc(), 46 | []string{"test", "-i=1"}, 47 | 17, 42, 48 | "test: not a valid number: =1\n", 49 | }, 50 | { 51 | loc(), 52 | []string{"test", "-i0x20"}, 53 | 0x20, 42, 54 | "", 55 | }, 56 | { 57 | loc(), 58 | []string{"test", "-i010"}, 59 | 8, 42, 60 | "", 61 | }, 62 | } 63 | 64 | func TestUint16(t *testing.T) { 65 | for x, tt := range uint16Tests { 66 | reset() 67 | i := Uint16('i', 17) 68 | opt := Uint16Long("uint16", 0, 42) 69 | if strings.Index(tt.where, ":-") > 0 { 70 | tt.where = fmt.Sprintf("#%d", x) 71 | } 72 | 73 | parse(tt.in) 74 | if s := checkError(tt.err); s != "" { 75 | t.Errorf("%s: %s", tt.where, s) 76 | } 77 | if got, want := *i, tt.i; got != want { 78 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 79 | } 80 | if got, want := *opt, tt.uint16; got != want { 81 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /uint32.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | type uint32Value uint32 13 | 14 | func (i *uint32Value) Set(value string, opt Option) error { 15 | v, err := strconv.ParseUint(value, 0, 32) 16 | if err != nil { 17 | if e, ok := err.(*strconv.NumError); ok { 18 | switch e.Err { 19 | case strconv.ErrRange: 20 | err = fmt.Errorf("value out of range: %s", value) 21 | case strconv.ErrSyntax: 22 | err = fmt.Errorf("not a valid number: %s", value) 23 | } 24 | } 25 | return err 26 | } 27 | *i = uint32Value(v) 28 | return nil 29 | } 30 | 31 | func (i *uint32Value) String() string { 32 | return strconv.FormatUint(uint64(*i), 10) 33 | } 34 | 35 | // Uint32 creates an option that parses its value as an uint32. 36 | func Uint32(name rune, value uint32, helpvalue ...string) *uint32 { 37 | return CommandLine.Uint32(name, value, helpvalue...) 38 | } 39 | 40 | func (s *Set) Uint32(name rune, value uint32, helpvalue ...string) *uint32 { 41 | return s.Uint32Long("", name, value, helpvalue...) 42 | } 43 | 44 | func Uint32Long(name string, short rune, value uint32, helpvalue ...string) *uint32 { 45 | return CommandLine.Uint32Long(name, short, value, helpvalue...) 46 | } 47 | 48 | func (s *Set) Uint32Long(name string, short rune, value uint32, helpvalue ...string) *uint32 { 49 | s.Uint32VarLong(&value, name, short, helpvalue...) 50 | return &value 51 | } 52 | 53 | func Uint32Var(p *uint32, name rune, helpvalue ...string) Option { 54 | return CommandLine.Uint32Var(p, name, helpvalue...) 55 | } 56 | 57 | func (s *Set) Uint32Var(p *uint32, name rune, helpvalue ...string) Option { 58 | return s.Uint32VarLong(p, "", name, helpvalue...) 59 | } 60 | 61 | func Uint32VarLong(p *uint32, name string, short rune, helpvalue ...string) Option { 62 | return CommandLine.Uint32VarLong(p, name, short, helpvalue...) 63 | } 64 | 65 | func (s *Set) Uint32VarLong(p *uint32, name string, short rune, helpvalue ...string) Option { 66 | return s.VarLong((*uint32Value)(p), name, short, helpvalue...) 67 | } 68 | -------------------------------------------------------------------------------- /uint32_test.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var uint32Tests = []struct { 14 | where string 15 | in []string 16 | i uint32 17 | uint32 uint32 18 | err string 19 | }{ 20 | { 21 | loc(), 22 | []string{}, 23 | 17, 42, 24 | "", 25 | }, 26 | { 27 | loc(), 28 | []string{"test", "-i", "1", "--uint32", "2"}, 29 | 1, 2, 30 | "", 31 | }, 32 | { 33 | loc(), 34 | []string{"test", "-i1", "--uint32=2"}, 35 | 1, 2, 36 | "", 37 | }, 38 | { 39 | loc(), 40 | []string{"test", "-i1", "-i2"}, 41 | 2, 42, 42 | "", 43 | }, 44 | { 45 | loc(), 46 | []string{"test", "-i=1"}, 47 | 17, 42, 48 | "test: not a valid number: =1\n", 49 | }, 50 | { 51 | loc(), 52 | []string{"test", "-i0x20"}, 53 | 0x20, 42, 54 | "", 55 | }, 56 | { 57 | loc(), 58 | []string{"test", "-i010"}, 59 | 8, 42, 60 | "", 61 | }, 62 | } 63 | 64 | func TestUint32(t *testing.T) { 65 | for x, tt := range uint32Tests { 66 | reset() 67 | i := Uint32('i', 17) 68 | opt := Uint32Long("uint32", 0, 42) 69 | if strings.Index(tt.where, ":-") > 0 { 70 | tt.where = fmt.Sprintf("#%d", x) 71 | } 72 | 73 | parse(tt.in) 74 | if s := checkError(tt.err); s != "" { 75 | t.Errorf("%s: %s", tt.where, s) 76 | } 77 | if got, want := *i, tt.i; got != want { 78 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 79 | } 80 | if got, want := *opt, tt.uint32; got != want { 81 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /uint64.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | type uint64Value uint64 13 | 14 | func (i *uint64Value) Set(value string, opt Option) error { 15 | v, err := strconv.ParseUint(value, 0, 64) 16 | if err != nil { 17 | if e, ok := err.(*strconv.NumError); ok { 18 | switch e.Err { 19 | case strconv.ErrRange: 20 | err = fmt.Errorf("value out of range: %s", value) 21 | case strconv.ErrSyntax: 22 | err = fmt.Errorf("not a valid number: %s", value) 23 | } 24 | } 25 | return err 26 | } 27 | *i = uint64Value(v) 28 | return nil 29 | } 30 | 31 | func (i *uint64Value) String() string { 32 | return strconv.FormatUint(uint64(*i), 10) 33 | } 34 | 35 | // Uint64 creates an option that parses its value as a uint64. 36 | func Uint64(name rune, value uint64, helpvalue ...string) *uint64 { 37 | return CommandLine.Uint64(name, value, helpvalue...) 38 | } 39 | 40 | func (s *Set) Uint64(name rune, value uint64, helpvalue ...string) *uint64 { 41 | return s.Uint64Long("", name, value, helpvalue...) 42 | } 43 | 44 | func Uint64Long(name string, short rune, value uint64, helpvalue ...string) *uint64 { 45 | return CommandLine.Uint64Long(name, short, value, helpvalue...) 46 | } 47 | 48 | func (s *Set) Uint64Long(name string, short rune, value uint64, helpvalue ...string) *uint64 { 49 | s.Uint64VarLong(&value, name, short, helpvalue...) 50 | return &value 51 | } 52 | 53 | func Uint64Var(p *uint64, name rune, helpvalue ...string) Option { 54 | return CommandLine.Uint64Var(p, name, helpvalue...) 55 | } 56 | 57 | func (s *Set) Uint64Var(p *uint64, name rune, helpvalue ...string) Option { 58 | return s.Uint64VarLong(p, "", name, helpvalue...) 59 | } 60 | 61 | func Uint64VarLong(p *uint64, name string, short rune, helpvalue ...string) Option { 62 | return CommandLine.Uint64VarLong(p, name, short, helpvalue...) 63 | } 64 | 65 | func (s *Set) Uint64VarLong(p *uint64, name string, short rune, helpvalue ...string) Option { 66 | return s.VarLong((*uint64Value)(p), name, short, helpvalue...) 67 | } 68 | -------------------------------------------------------------------------------- /uint64_test.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var uint64Tests = []struct { 14 | where string 15 | in []string 16 | i uint64 17 | uint64 uint64 18 | err string 19 | }{ 20 | { 21 | loc(), 22 | []string{}, 23 | 17, 42, 24 | "", 25 | }, 26 | { 27 | loc(), 28 | []string{"test", "-i", "1", "--uint64", "2"}, 29 | 1, 2, 30 | "", 31 | }, 32 | { 33 | loc(), 34 | []string{"test", "-i1", "--uint64=2"}, 35 | 1, 2, 36 | "", 37 | }, 38 | { 39 | loc(), 40 | []string{"test", "-i1", "-i2"}, 41 | 2, 42, 42 | "", 43 | }, 44 | { 45 | loc(), 46 | []string{"test", "-i=1"}, 47 | 17, 42, 48 | "test: not a valid number: =1\n", 49 | }, 50 | { 51 | loc(), 52 | []string{"test", "-i0x20"}, 53 | 0x20, 42, 54 | "", 55 | }, 56 | { 57 | loc(), 58 | []string{"test", "-i010"}, 59 | 8, 42, 60 | "", 61 | }, 62 | } 63 | 64 | func TestUint64(t *testing.T) { 65 | for x, tt := range uint64Tests { 66 | reset() 67 | i := Uint64('i', 17) 68 | opt := Uint64Long("uint64", 0, 42) 69 | if strings.Index(tt.where, ":-") > 0 { 70 | tt.where = fmt.Sprintf("#%d", x) 71 | } 72 | 73 | parse(tt.in) 74 | if s := checkError(tt.err); s != "" { 75 | t.Errorf("%s: %s", tt.where, s) 76 | } 77 | if got, want := *i, tt.i; got != want { 78 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 79 | } 80 | if got, want := *opt, tt.uint64; got != want { 81 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /uint_test.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var uintTests = []struct { 14 | where string 15 | in []string 16 | i uint 17 | uint uint 18 | err string 19 | }{ 20 | { 21 | loc(), 22 | []string{}, 23 | 17, 42, 24 | "", 25 | }, 26 | { 27 | loc(), 28 | []string{"test", "-i", "1", "--uint", "2"}, 29 | 1, 2, 30 | "", 31 | }, 32 | { 33 | loc(), 34 | []string{"test", "-i1", "--uint=2"}, 35 | 1, 2, 36 | "", 37 | }, 38 | { 39 | loc(), 40 | []string{"test", "-i1", "-i2"}, 41 | 2, 42, 42 | "", 43 | }, 44 | { 45 | loc(), 46 | []string{"test", "-i=1"}, 47 | 17, 42, 48 | "test: not a valid number: =1\n", 49 | }, 50 | { 51 | loc(), 52 | []string{"test", "-i0x20"}, 53 | 0x20, 42, 54 | "", 55 | }, 56 | { 57 | loc(), 58 | []string{"test", "-i010"}, 59 | 8, 42, 60 | "", 61 | }, 62 | } 63 | 64 | func TestUint(t *testing.T) { 65 | for x, tt := range uintTests { 66 | reset() 67 | i := Uint('i', 17) 68 | opt := UintLong("uint", 0, 42) 69 | if strings.Index(tt.where, ":-") > 0 { 70 | tt.where = fmt.Sprintf("#%d", x) 71 | } 72 | 73 | parse(tt.in) 74 | if s := checkError(tt.err); s != "" { 75 | t.Errorf("%s: %s", tt.where, s) 76 | } 77 | if got, want := *i, tt.i; got != want { 78 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 79 | } 80 | if got, want := *opt, tt.uint; got != want { 81 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /unsigned.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | type unsigned uint64 13 | 14 | type UnsignedLimit struct { 15 | Base int // Base for conversion as per strconv.ParseInt 16 | Bits int // Number of bits as per strconv.ParseInt 17 | Min uint64 // Minimum allowed value if both Min and Max are not 0 18 | Max uint64 // Maximum allowed value if both Min and Max are not 0 19 | } 20 | 21 | var unsignedLimits = make(map[*unsigned]*UnsignedLimit) 22 | 23 | func (n *unsigned) Set(value string, opt Option) error { 24 | l := unsignedLimits[n] 25 | if l == nil { 26 | return fmt.Errorf("no limits defined for %s", opt.Name()) 27 | } 28 | v, err := strconv.ParseUint(value, l.Base, l.Bits) 29 | if err != nil { 30 | if e, ok := err.(*strconv.NumError); ok { 31 | switch e.Err { 32 | case strconv.ErrRange: 33 | err = fmt.Errorf("value out of range: %s", value) 34 | case strconv.ErrSyntax: 35 | err = fmt.Errorf("not a valid number: %s", value) 36 | } 37 | } 38 | return err 39 | } 40 | if l.Min != 0 || l.Max != 0 { 41 | if v < l.Min { 42 | return fmt.Errorf("value out of range (<%v): %s", l.Min, value) 43 | } 44 | if v > l.Max { 45 | return fmt.Errorf("value out of range (>%v): %s", l.Max, value) 46 | } 47 | } 48 | *n = unsigned(v) 49 | return nil 50 | } 51 | 52 | func (n *unsigned) String() string { 53 | l := unsignedLimits[n] 54 | if l != nil && l.Base != 0 { 55 | return strconv.FormatUint(uint64(*n), l.Base) 56 | } 57 | return strconv.FormatUint(uint64(*n), 10) 58 | } 59 | 60 | // Unsigned creates an option that is stored in a uint64 and is 61 | // constrained by the limits pointed to by l. The Max and Min values are only 62 | // used if at least one of the values are not 0. If Base is 0, the base is 63 | // implied by the string's prefix: base 16 for "0x", base 8 for "0", and base 64 | // 10 otherwise. 65 | func Unsigned(name rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 { 66 | return CommandLine.Unsigned(name, value, l, helpvalue...) 67 | } 68 | 69 | func (s *Set) Unsigned(name rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 { 70 | return s.UnsignedLong("", name, value, l, helpvalue...) 71 | } 72 | 73 | func UnsignedLong(name string, short rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 { 74 | return CommandLine.UnsignedLong(name, short, value, l, helpvalue...) 75 | } 76 | 77 | func (s *Set) UnsignedLong(name string, short rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 { 78 | s.UnsignedVarLong(&value, name, short, l, helpvalue...) 79 | return &value 80 | } 81 | 82 | func UnsignedVar(p *uint64, name rune, l *UnsignedLimit, helpvalue ...string) Option { 83 | return CommandLine.UnsignedVar(p, name, l, helpvalue...) 84 | } 85 | 86 | func (s *Set) UnsignedVar(p *uint64, name rune, l *UnsignedLimit, helpvalue ...string) Option { 87 | return s.UnsignedVarLong(p, "", name, l, helpvalue...) 88 | } 89 | 90 | func UnsignedVarLong(p *uint64, name string, short rune, l *UnsignedLimit, helpvalue ...string) Option { 91 | return CommandLine.UnsignedVarLong(p, name, short, l, helpvalue...) 92 | } 93 | 94 | func (s *Set) UnsignedVarLong(p *uint64, name string, short rune, l *UnsignedLimit, helpvalue ...string) Option { 95 | opt := s.VarLong((*unsigned)(p), name, short, helpvalue...) 96 | if l.Base > 36 || l.Base == 1 || l.Base < 0 { 97 | fmt.Fprintf(stderr, "invalid base for %s: %d\n", opt.Name(), l.Base) 98 | exit(1) 99 | } 100 | if l.Bits < 0 || l.Bits > 64 { 101 | fmt.Fprintf(stderr, "invalid bit size for %s: %d\n", opt.Name(), l.Bits) 102 | exit(1) 103 | } 104 | if l.Min > l.Max { 105 | fmt.Fprintf(stderr, "min greater than max for %s\n", opt.Name()) 106 | exit(1) 107 | } 108 | lim := *l 109 | unsignedLimits[(*unsigned)(p)] = &lim 110 | return opt 111 | } 112 | -------------------------------------------------------------------------------- /unsigned_test.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var unsignedTests = []struct { 14 | where string 15 | in []string 16 | l UnsignedLimit 17 | out uint64 18 | err string 19 | }{ 20 | { 21 | where: loc(), 22 | }, 23 | { 24 | loc(), 25 | []string{"test", "-n", "1010"}, 26 | UnsignedLimit{Base: 2, Bits: 5}, 27 | 10, 28 | "", 29 | }, 30 | { 31 | loc(), 32 | []string{"test", "-n", "1010"}, 33 | UnsignedLimit{Base: 2, Bits: 4}, 34 | 10, 35 | "", 36 | }, 37 | { 38 | loc(), 39 | []string{"test", "-n", "1010"}, 40 | UnsignedLimit{Base: 2, Bits: 3}, 41 | 0, 42 | "test: value out of range: 1010\n", 43 | }, 44 | { 45 | loc(), 46 | []string{"test", "-n", "3"}, 47 | UnsignedLimit{Min: 4, Max: 6}, 48 | 0, 49 | "test: value out of range (<4): 3\n", 50 | }, 51 | { 52 | loc(), 53 | []string{"test", "-n", "4"}, 54 | UnsignedLimit{Min: 4, Max: 6}, 55 | 4, 56 | "", 57 | }, 58 | { 59 | loc(), 60 | []string{"test", "-n", "5"}, 61 | UnsignedLimit{Min: 4, Max: 6}, 62 | 5, 63 | "", 64 | }, 65 | { 66 | loc(), 67 | []string{"test", "-n", "6"}, 68 | UnsignedLimit{Min: 4, Max: 6}, 69 | 6, 70 | "", 71 | }, 72 | { 73 | loc(), 74 | []string{"test", "-n", "7"}, 75 | UnsignedLimit{Min: 4, Max: 6}, 76 | 0, 77 | "test: value out of range (>6): 7\n", 78 | }, 79 | } 80 | 81 | func TestUnsigneds(t *testing.T) { 82 | for x, tt := range unsignedTests { 83 | if strings.Index(tt.where, ":-") > 0 { 84 | tt.where = fmt.Sprintf("#%d", x) 85 | } 86 | 87 | reset() 88 | n := Unsigned('n', 0, &tt.l) 89 | parse(tt.in) 90 | if s := checkError(tt.err); s != "" { 91 | t.Errorf("%s: %s", tt.where, s) 92 | } 93 | if *n != tt.out { 94 | t.Errorf("%s: got %v, want %v", tt.where, *n, tt.out) 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /util_test.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "path" 11 | "reflect" 12 | "runtime" 13 | "strings" 14 | ) 15 | 16 | var errorString string 17 | 18 | func reset() { 19 | CommandLine.shortOptions = make(map[rune]*option) 20 | CommandLine.longOptions = make(map[string]*option) 21 | CommandLine.options = nil 22 | CommandLine.args = nil 23 | CommandLine.program = "" 24 | errorString = "" 25 | } 26 | 27 | func parse(args []string) { 28 | err := CommandLine.Getopt(args, nil) 29 | if err != nil { 30 | b := &bytes.Buffer{} 31 | 32 | fmt.Fprintln(b, CommandLine.program+": "+err.Error()) 33 | CommandLine.PrintUsage(b) 34 | errorString = b.String() 35 | } 36 | } 37 | 38 | func badSlice(a, b []string) bool { 39 | if len(a) != len(b) { 40 | return true 41 | } 42 | for x, v := range a { 43 | if b[x] != v { 44 | return true 45 | } 46 | } 47 | return false 48 | } 49 | 50 | func loc() string { 51 | _, file, line, _ := runtime.Caller(1) 52 | return fmt.Sprintf("%s:%d", path.Base(file), line) 53 | } 54 | 55 | func (o *option) Equal(opt *option) bool { 56 | if o.value != nil && opt.value == nil { 57 | return false 58 | } 59 | if o.value == nil && opt.value != nil { 60 | return false 61 | } 62 | if o.value != nil && o.value.String() != opt.value.String() { 63 | return false 64 | } 65 | 66 | oc := *o 67 | optc := *opt 68 | oc.value = nil 69 | optc.value = nil 70 | return reflect.DeepEqual(&oc, &optc) 71 | } 72 | 73 | func newStringValue(s string) *stringValue { return (*stringValue)(&s) } 74 | 75 | func checkError(err string) string { 76 | switch { 77 | case err == errorString: 78 | return "" 79 | case err == "": 80 | return fmt.Sprintf("unexpected error %q", errorString) 81 | case errorString == "": 82 | return fmt.Sprintf("did not get expected error %q", err) 83 | case !strings.HasPrefix(errorString, err): 84 | return fmt.Sprintf("got error %q, want %q", errorString, err) 85 | } 86 | return "" 87 | } 88 | -------------------------------------------------------------------------------- /v2/bool.go: -------------------------------------------------------------------------------- 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 | 5 | package getopt 6 | 7 | // Bool creates a flag option that is a bool. Bools normally do not take a 8 | // value however one can be assigned by using the long form of the option: 9 | // 10 | // --option=true 11 | // --o=false 12 | // 13 | // The value is case insensitive and one of true, false, t, f, on, off, t and 0. 14 | func Bool(name rune, helpvalue ...string) *bool { 15 | var b bool 16 | CommandLine.Flag(&b, name, helpvalue...) 17 | return &b 18 | } 19 | 20 | func BoolLong(name string, short rune, helpvalue ...string) *bool { 21 | var p bool 22 | CommandLine.FlagLong(&p, name, short, helpvalue...) 23 | return &p 24 | } 25 | 26 | func (s *Set) Bool(name rune, helpvalue ...string) *bool { 27 | var b bool 28 | s.Flag(&b, name, helpvalue...) 29 | return &b 30 | } 31 | 32 | func (s *Set) BoolLong(name string, short rune, helpvalue ...string) *bool { 33 | var p bool 34 | s.FlagLong(&p, name, short, helpvalue...) 35 | return &p 36 | } 37 | -------------------------------------------------------------------------------- /v2/bool_test.go: -------------------------------------------------------------------------------- 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 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var boolTests = []struct { 14 | where string 15 | in []string 16 | f bool 17 | fc int 18 | opt bool 19 | optc int 20 | err string 21 | }{ 22 | { 23 | loc(), 24 | []string{}, 25 | false, 0, 26 | false, 0, 27 | "", 28 | }, 29 | { 30 | loc(), 31 | []string{"test", "-f", "--opt"}, 32 | true, 1, 33 | true, 1, 34 | "", 35 | }, 36 | { 37 | loc(), 38 | []string{"test", "--f", "--opt"}, 39 | true, 1, 40 | true, 1, 41 | "", 42 | }, 43 | { 44 | loc(), 45 | []string{"test", "-ff", "-f", "--opt", "--opt"}, 46 | true, 3, 47 | true, 2, 48 | "", 49 | }, 50 | { 51 | loc(), 52 | []string{"test", "--opt", "--opt=false"}, 53 | false, 0, 54 | false, 2, 55 | "", 56 | }, 57 | { 58 | loc(), 59 | []string{"test", "-f", "false"}, 60 | true, 1, 61 | false, 0, 62 | "", 63 | }, 64 | { 65 | loc(), 66 | []string{"test", "-f=false"}, 67 | true, 1, 68 | false, 0, 69 | "test: unknown option: -=\n", 70 | }, 71 | { 72 | loc(), 73 | []string{"test", "-f", "false"}, 74 | true, 1, 75 | false, 0, 76 | "", 77 | }, 78 | } 79 | 80 | func TestBool(t *testing.T) { 81 | for x, tt := range boolTests { 82 | reset() 83 | f := Bool('f') 84 | opt := BoolLong("opt", 0) 85 | if strings.Index(tt.where, ":-") > 0 { 86 | tt.where = fmt.Sprintf("#%d", x) 87 | } 88 | 89 | parse(tt.in) 90 | if s := checkError(tt.err); s != "" { 91 | t.Errorf("%s: %s", tt.where, s) 92 | } 93 | if got, want := *f, tt.f; got != want { 94 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 95 | } 96 | if got, want := *opt, tt.opt; got != want { 97 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 98 | } 99 | if got, want := GetCount('f'), tt.fc; got != want { 100 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 101 | } 102 | if got, want := GetCount("opt"), tt.optc; got != want { 103 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /v2/breakup_test.go: -------------------------------------------------------------------------------- 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 | 5 | package getopt 6 | 7 | import ( 8 | "testing" 9 | ) 10 | 11 | var breakupTests = []struct { 12 | in string 13 | max int 14 | out []string 15 | }{ 16 | {"", 8, []string{}}, 17 | {"a fox", 8, []string{"a fox"}}, 18 | {"a foxhound is sly", 2, []string{"a", "foxhound", "is", "sly"}}, 19 | {"a foxhound is sly", 5, []string{"a", "foxhound", "is", "sly"}}, 20 | {"a foxhound is sly", 6, []string{"a", "foxhound", "is sly"}}, 21 | {"a foxhound is sly", 7, []string{"a", "foxhound", "is sly"}}, 22 | {"a foxhound is sly", 8, []string{"a", "foxhound", "is sly"}}, 23 | {"a foxhound is sly", 9, []string{"a", "foxhound", "is sly"}}, 24 | {"a foxhound is sly", 10, []string{"a foxhound", "is sly"}}, 25 | } 26 | 27 | func TestBreakup(t *testing.T) { 28 | for x, tt := range breakupTests { 29 | out := breakup(tt.in, tt.max) 30 | if badSlice(out, tt.out) { 31 | t.Errorf("#%d: got %v, want %v", x, out, tt.out) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /v2/counter.go: -------------------------------------------------------------------------------- 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 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | type counterValue int 13 | 14 | func (b *counterValue) Set(value string, opt Option) error { 15 | if value == "" { 16 | *b++ 17 | } else { 18 | v, err := strconv.ParseInt(value, 0, strconv.IntSize) 19 | if err != nil { 20 | if e, ok := err.(*strconv.NumError); ok { 21 | switch e.Err { 22 | case strconv.ErrRange: 23 | err = fmt.Errorf("value out of range: %s", value) 24 | case strconv.ErrSyntax: 25 | err = fmt.Errorf("not a valid number: %s", value) 26 | } 27 | } 28 | return err 29 | } 30 | *b = counterValue(v) 31 | } 32 | return nil 33 | } 34 | 35 | func (b *counterValue) String() string { 36 | return strconv.Itoa(int(*b)) 37 | } 38 | 39 | // Counter creates a counting flag stored as an int. Each time the option 40 | // is seen while parsing the value is incremented. The value of the counter 41 | // may be explicitly set by using the long form: 42 | // 43 | // --counter=5 44 | // --c=5 45 | // 46 | // Further instances of the option will increment from the set value. 47 | func Counter(name rune, helpvalue ...string) *int { 48 | var p int 49 | CommandLine.FlagLong((*counterValue)(&p), "", name, helpvalue...).SetFlag() 50 | return &p 51 | } 52 | 53 | func (s *Set) Counter(name rune, helpvalue ...string) *int { 54 | var p int 55 | s.FlagLong((*counterValue)(&p), "", name, helpvalue...).SetFlag() 56 | return &p 57 | } 58 | 59 | func CounterLong(name string, short rune, helpvalue ...string) *int { 60 | var p int 61 | CommandLine.FlagLong((*counterValue)(&p), name, short, helpvalue...).SetFlag() 62 | return &p 63 | } 64 | 65 | func (s *Set) CounterLong(name string, short rune, helpvalue ...string) *int { 66 | var p int 67 | s.FlagLong((*counterValue)(&p), name, short, helpvalue...).SetFlag() 68 | return &p 69 | } 70 | -------------------------------------------------------------------------------- /v2/counter_test.go: -------------------------------------------------------------------------------- 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 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var counterTests = []struct { 14 | where string 15 | in []string 16 | c int 17 | cnt int 18 | err string 19 | }{ 20 | { 21 | loc(), 22 | []string{}, 23 | 0, 24 | 0, 25 | "", 26 | }, 27 | { 28 | loc(), 29 | []string{"test", "-c", "--cnt"}, 30 | 1, 31 | 1, 32 | "", 33 | }, 34 | { 35 | loc(), 36 | []string{"test", "-cc", "-c", "--cnt", "--cnt"}, 37 | 3, 38 | 2, 39 | "", 40 | }, 41 | { 42 | loc(), 43 | []string{"test", "--c=17", "--cnt=42"}, 44 | 17, 45 | 42, 46 | "", 47 | }, 48 | { 49 | loc(), 50 | []string{"test", "--cnt=false"}, 51 | 0, 0, 52 | "test: not a valid number: false\n", 53 | }, 54 | } 55 | 56 | func TestCounter(t *testing.T) { 57 | for x, tt := range counterTests { 58 | reset() 59 | c := Counter('c') 60 | cnt := CounterLong("cnt", 0) 61 | if strings.Index(tt.where, ":-") > 0 { 62 | tt.where = fmt.Sprintf("#%d", x) 63 | } 64 | 65 | parse(tt.in) 66 | if s := checkError(tt.err); s != "" { 67 | t.Errorf("%s: %s", tt.where, s) 68 | } 69 | if got, want := *c, tt.c; got != want { 70 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 71 | } 72 | if got, want := *cnt, tt.cnt; got != want { 73 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /v2/duration.go: -------------------------------------------------------------------------------- 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 | 5 | package getopt 6 | 7 | import "time" 8 | 9 | // Duration creates an option that parses its value as a time.Duration. 10 | func Duration(name rune, value time.Duration, helpvalue ...string) *time.Duration { 11 | CommandLine.FlagLong(&value, "", name, helpvalue...) 12 | return &value 13 | } 14 | 15 | func (s *Set) Duration(name rune, value time.Duration, helpvalue ...string) *time.Duration { 16 | s.FlagLong(&value, "", name, helpvalue...) 17 | return &value 18 | } 19 | 20 | func DurationLong(name string, short rune, value time.Duration, helpvalue ...string) *time.Duration { 21 | CommandLine.FlagLong(&value, name, short, helpvalue...) 22 | return &value 23 | } 24 | 25 | func (s *Set) DurationLong(name string, short rune, value time.Duration, helpvalue ...string) *time.Duration { 26 | s.FlagLong(&value, name, short, helpvalue...) 27 | return &value 28 | } 29 | -------------------------------------------------------------------------------- /v2/duration_test.go: -------------------------------------------------------------------------------- 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 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | "time" 12 | ) 13 | 14 | var durationTests = []struct { 15 | where string 16 | in []string 17 | d time.Duration 18 | dur time.Duration 19 | err string 20 | }{ 21 | { 22 | loc(), 23 | []string{}, 24 | 17, 42, 25 | "", 26 | }, 27 | { 28 | loc(), 29 | []string{"test", "-d", "1s", "--duration", "2s"}, 30 | time.Second, 2 * time.Second, 31 | "", 32 | }, 33 | { 34 | loc(), 35 | []string{"test", "-d1s", "-d2s"}, 36 | 2 * time.Second, 42, 37 | "", 38 | }, 39 | { 40 | loc(), 41 | []string{"test", "-d1"}, 42 | 17, 42, 43 | "test: time: missing unit in duration", 44 | }, 45 | { 46 | loc(), 47 | []string{"test", "--duration", "foo"}, 48 | 17, 42, 49 | "test: time: invalid duration", 50 | }, 51 | } 52 | 53 | func TestDuration(t *testing.T) { 54 | for x, tt := range durationTests { 55 | reset() 56 | d := Duration('d', 17) 57 | opt := DurationLong("duration", 0, 42) 58 | if strings.Index(tt.where, ":-") > 0 { 59 | tt.where = fmt.Sprintf("#%d", x) 60 | } 61 | 62 | parse(tt.in) 63 | if s := checkError(tt.err); s != "" { 64 | t.Errorf("%s: %s", tt.where, s) 65 | } 66 | if got, want := *d, tt.d; got != want { 67 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 68 | } 69 | if got, want := *opt, tt.dur; got != want { 70 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /v2/enum.go: -------------------------------------------------------------------------------- 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 | 5 | package getopt 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | "sync" 11 | ) 12 | 13 | type enumValue string 14 | 15 | var ( 16 | enumValuesMu sync.Mutex 17 | enumValues = make(map[*enumValue]map[string]struct{}) 18 | ) 19 | 20 | func (s *enumValue) Set(value string, opt Option) error { 21 | enumValuesMu.Lock() 22 | es, ok := enumValues[s] 23 | enumValuesMu.Unlock() 24 | if !ok || es == nil { 25 | return errors.New("this option has no values") 26 | } 27 | if _, ok := es[value]; !ok { 28 | return errors.New("invalid value: " + value) 29 | } 30 | *s = enumValue(value) 31 | return nil 32 | } 33 | 34 | func (s *enumValue) String() string { 35 | return string(*s) 36 | } 37 | 38 | // Enum creates an option that can only be set to one of the enumerated strings 39 | // passed in values. Passing nil or an empty slice results in an option that 40 | // will always fail. If not "", value is the default value of the enum. If 41 | // value is not listed in values then Enum will produce an error on standard 42 | // error and then exit the program with a status of 1. 43 | func Enum(name rune, values []string, value string, helpvalue ...string) *string { 44 | return CommandLine.Enum(name, values, value, helpvalue...) 45 | } 46 | 47 | func (s *Set) Enum(name rune, values []string, value string, helpvalue ...string) *string { 48 | var p enumValue 49 | p.define(values, value, &option{short: name}) 50 | s.FlagLong(&p, "", name, helpvalue...) 51 | return (*string)(&p) 52 | } 53 | 54 | func EnumLong(name string, short rune, values []string, value string, helpvalue ...string) *string { 55 | return CommandLine.EnumLong(name, short, values, value, helpvalue...) 56 | } 57 | 58 | func (s *Set) EnumLong(name string, short rune, values []string, value string, helpvalue ...string) *string { 59 | var p enumValue 60 | p.define(values, value, &option{short: short, long: name}) 61 | s.FlagLong(&p, name, short, helpvalue...) 62 | return (*string)(&p) 63 | } 64 | 65 | func (e *enumValue) define(values []string, def string, opt Option) { 66 | m := make(map[string]struct{}) 67 | for _, v := range values { 68 | m[v] = struct{}{} 69 | } 70 | enumValuesMu.Lock() 71 | enumValues[e] = m 72 | enumValuesMu.Unlock() 73 | if def != "" { 74 | if err := e.Set(def, nil); err != nil { 75 | fmt.Fprintf(stderr, "setting default for %s: %v\n", opt.Name(), err) 76 | exit(1) 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /v2/enum_test.go: -------------------------------------------------------------------------------- 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 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var enumTests = []struct { 14 | where string 15 | in []string 16 | values []string 17 | def string 18 | out string 19 | err string 20 | }{ 21 | { 22 | loc(), 23 | nil, 24 | []string{}, 25 | "", 26 | "", 27 | "", 28 | }, 29 | { 30 | loc(), 31 | []string{"test", "-e", "val1"}, 32 | []string{"val1", "val2"}, 33 | "", 34 | "val1", 35 | "", 36 | }, 37 | { 38 | loc(), 39 | []string{"test", "-e", "val1", "-e", "val2"}, 40 | []string{"val1", "val2"}, 41 | "", 42 | "val2", 43 | "", 44 | }, 45 | { 46 | loc(), 47 | []string{"test"}, 48 | []string{"val1", "val2"}, 49 | "val2", 50 | "val2", 51 | "", 52 | }, 53 | { 54 | loc(), 55 | []string{"test", "-e", "val3"}, 56 | []string{"val1", "val2"}, 57 | "", 58 | "", 59 | "test: invalid value: val3\n", 60 | }, 61 | } 62 | 63 | func TestEnum(t *testing.T) { 64 | for x, tt := range enumTests { 65 | if strings.Index(tt.where, ":-") > 0 { 66 | tt.where = fmt.Sprintf("#%d", x) 67 | } 68 | 69 | reset() 70 | e := Enum('e', tt.values, tt.def) 71 | parse(tt.in) 72 | if s := checkError(tt.err); s != "" { 73 | t.Errorf("%s: %s", tt.where, s) 74 | } 75 | if *e != tt.out { 76 | t.Errorf("%s: got %v, want %v", tt.where, *e, tt.out) 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /v2/error.go: -------------------------------------------------------------------------------- 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 | 5 | package getopt 6 | 7 | import "fmt" 8 | 9 | // An Error is returned by Getopt when it encounters an error. 10 | type Error struct { 11 | ErrorCode // General reason of failure. 12 | Err error // The actual error. 13 | Parameter string // Parameter passed to option, if any 14 | Name string // Option that cause error, if any 15 | } 16 | 17 | // Error returns the error message, implementing the error interface. 18 | func (i *Error) Error() string { return i.Err.Error() } 19 | 20 | // An ErrorCode indicates what sort of error was encountered. 21 | type ErrorCode int 22 | 23 | const ( 24 | NoError = ErrorCode(iota) 25 | UnknownOption // an invalid option was encountered 26 | MissingParameter // the options parameter is missing 27 | ExtraParameter // a value was set to a long flag 28 | Invalid // attempt to set an invalid value 29 | ) 30 | 31 | func (e ErrorCode) String() string { 32 | switch e { 33 | case UnknownOption: 34 | return "unknow option" 35 | case MissingParameter: 36 | return "missing argument" 37 | case ExtraParameter: 38 | return "unxpected value" 39 | case Invalid: 40 | return "error setting value" 41 | } 42 | return "unknown error" 43 | } 44 | 45 | // unknownOption returns an Error indicating an unknown option was 46 | // encountered. 47 | func unknownOption(name interface{}) *Error { 48 | i := &Error{ErrorCode: UnknownOption} 49 | switch n := name.(type) { 50 | case rune: 51 | if n == '-' { 52 | i.Name = "-" 53 | } else { 54 | i.Name = "-" + string(n) 55 | } 56 | case string: 57 | i.Name = "--" + n 58 | } 59 | i.Err = fmt.Errorf("unknown option: %s", i.Name) 60 | return i 61 | } 62 | 63 | // missingArg returns an Error inidicating option o was not passed 64 | // a required paramter. 65 | func missingArg(o Option) *Error { 66 | return &Error{ 67 | ErrorCode: MissingParameter, 68 | Name: o.Name(), 69 | Err: fmt.Errorf("missing parameter for %s", o.Name()), 70 | } 71 | } 72 | 73 | // extraArg returns an Error inidicating option o was passed the 74 | // unexpected paramter value. 75 | func extraArg(o Option, value string) *Error { 76 | return &Error{ 77 | ErrorCode: ExtraParameter, 78 | Name: o.Name(), 79 | Parameter: value, 80 | Err: fmt.Errorf("unexpected parameter passed to %s: %q", o.Name(), value), 81 | } 82 | } 83 | 84 | // setError returns an Error inidicating option o and the specified 85 | // error while setting it to value. 86 | func setError(o Option, value string, err error) *Error { 87 | return &Error{ 88 | ErrorCode: Invalid, 89 | Name: o.Name(), 90 | Parameter: value, 91 | Err: err, 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /v2/generic.go: -------------------------------------------------------------------------------- 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 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | "strings" 11 | "time" 12 | ) 13 | 14 | type generic struct { 15 | p interface{} 16 | } 17 | 18 | // Flag is shorthand for CommandLine.Flag. 19 | func Flag(v interface{}, short rune, helpvalue ...string) Option { 20 | return CommandLine.long(v, "", short, helpvalue...) 21 | } 22 | 23 | // FlagLong is shorthand for CommandLine.LongFlag. 24 | func FlagLong(v interface{}, long string, short rune, helpvalue ...string) Option { 25 | return CommandLine.long(v, long, short, helpvalue...) 26 | } 27 | 28 | // Flag calls FlagLong with only a short flag name. 29 | func (s *Set) Flag(v interface{}, short rune, helpvalue ...string) Option { 30 | return s.long(v, "", short, helpvalue...) 31 | } 32 | 33 | // FlagLong returns an Option in Set s for setting v. If long is not "" then 34 | // the option has a long name, and if short is not 0, the option has a short 35 | // name. v must either be of type getopt.Value or a pointer to one of the 36 | // supported builtin types: 37 | // 38 | // bool, string, []string 39 | // int, int8, int16, int32, int64 40 | // uint, uint8, uint16, uint32, uint64 41 | // float32, float64 42 | // time.Duration 43 | // 44 | // FlagLong will panic if v is not a getopt.Value or one of the supported 45 | // builtin types. 46 | // 47 | // The default value of the flag is the value of v at the time FlagLong is 48 | // called. 49 | func (s *Set) FlagLong(v interface{}, long string, short rune, helpvalue ...string) Option { 50 | return s.long(v, long, short, helpvalue...) 51 | } 52 | 53 | func (s *Set) long(v interface{}, long string, short rune, helpvalue ...string) (opt Option) { 54 | // Fix up our location when we return. 55 | if where := calledFrom(); where != "" { 56 | defer func() { 57 | if opt, ok := opt.(*option); ok { 58 | opt.where = where 59 | } 60 | }() 61 | } 62 | switch p := v.(type) { 63 | case Value: 64 | return s.addFlag(p, long, short, helpvalue...) 65 | case *bool: 66 | return s.addFlag(&generic{v}, long, short, helpvalue...).SetFlag() 67 | case *string, *[]string: 68 | return s.addFlag(&generic{v}, long, short, helpvalue...) 69 | case *int, *int8, *int16, *int32, *int64: 70 | return s.addFlag(&generic{v}, long, short, helpvalue...) 71 | case *uint, *uint8, *uint16, *uint32, *uint64: 72 | return s.addFlag(&generic{v}, long, short, helpvalue...) 73 | case *float32, *float64: 74 | return s.addFlag(&generic{v}, long, short, helpvalue...) 75 | case *time.Duration: 76 | return s.addFlag(&generic{v}, long, short, helpvalue...) 77 | default: 78 | panic(fmt.Sprintf("unsupported flag type: %T", v)) 79 | } 80 | } 81 | 82 | // genericValue returns the object underlying the generic Value v, or nil if v 83 | // is not a generic Value. 84 | func genericValue(v Value) interface{} { 85 | if g, ok := v.(*generic); ok { 86 | return g.p 87 | } 88 | return nil 89 | } 90 | 91 | func (g *generic) Set(value string, opt Option) error { 92 | strconvErr := func(err error) error { 93 | if e, ok := err.(*strconv.NumError); ok { 94 | switch e.Err { 95 | case strconv.ErrRange: 96 | err = fmt.Errorf("value out of range: %s", value) 97 | case strconv.ErrSyntax: 98 | err = fmt.Errorf("not a valid number: %s", value) 99 | } 100 | } 101 | return err 102 | } 103 | switch p := g.p.(type) { 104 | case *bool: 105 | switch strings.ToLower(value) { 106 | case "", "1", "true", "on", "t": 107 | *p = true 108 | case "0", "false", "off", "f": 109 | *p = false 110 | default: 111 | return fmt.Errorf("invalid value for bool %s: %q", opt.Name(), value) 112 | } 113 | return nil 114 | case *string: 115 | *p = value 116 | return nil 117 | case *[]string: 118 | a := strings.Split(value, ",") 119 | // If this is the first time we are seen then nil out the 120 | // default value. 121 | if opt.Count() <= 1 { 122 | *p = nil 123 | } 124 | *p = append(*p, a...) 125 | return nil 126 | case *int: 127 | i64, err := strconv.ParseInt(value, 0, strconv.IntSize) 128 | if err == nil { 129 | *p = int(i64) 130 | } 131 | return strconvErr(err) 132 | case *int8: 133 | i64, err := strconv.ParseInt(value, 0, 8) 134 | if err == nil { 135 | *p = int8(i64) 136 | } 137 | return strconvErr(err) 138 | case *int16: 139 | i64, err := strconv.ParseInt(value, 0, 16) 140 | if err == nil { 141 | *p = int16(i64) 142 | } 143 | return strconvErr(err) 144 | case *int32: 145 | i64, err := strconv.ParseInt(value, 0, 32) 146 | if err == nil { 147 | *p = int32(i64) 148 | } 149 | return strconvErr(err) 150 | case *int64: 151 | i64, err := strconv.ParseInt(value, 0, 64) 152 | if err == nil { 153 | *p = i64 154 | } 155 | return strconvErr(err) 156 | case *uint: 157 | u64, err := strconv.ParseUint(value, 0, strconv.IntSize) 158 | if err == nil { 159 | *p = uint(u64) 160 | } 161 | return strconvErr(err) 162 | case *uint8: 163 | u64, err := strconv.ParseUint(value, 0, 8) 164 | if err == nil { 165 | *p = uint8(u64) 166 | } 167 | return strconvErr(err) 168 | case *uint16: 169 | u64, err := strconv.ParseUint(value, 0, 16) 170 | if err == nil { 171 | *p = uint16(u64) 172 | } 173 | return strconvErr(err) 174 | case *uint32: 175 | u64, err := strconv.ParseUint(value, 0, 32) 176 | if err == nil { 177 | *p = uint32(u64) 178 | } 179 | return strconvErr(err) 180 | case *uint64: 181 | u64, err := strconv.ParseUint(value, 0, 64) 182 | if err == nil { 183 | *p = u64 184 | } 185 | return strconvErr(err) 186 | case *float32: 187 | f64, err := strconv.ParseFloat(value, 32) 188 | if err == nil { 189 | *p = float32(f64) 190 | } 191 | return strconvErr(err) 192 | case *float64: 193 | f64, err := strconv.ParseFloat(value, 64) 194 | if err == nil { 195 | *p = f64 196 | } 197 | return strconvErr(err) 198 | case *time.Duration: 199 | v, err := time.ParseDuration(value) 200 | if err == nil { 201 | *p = v 202 | } 203 | return err 204 | } 205 | panic("internal error") 206 | } 207 | 208 | func (g *generic) String() string { 209 | switch p := g.p.(type) { 210 | case *bool: 211 | if *p { 212 | return "true" 213 | } 214 | return "false" 215 | case *string: 216 | return *p 217 | case *[]string: 218 | return strings.Join([]string(*p), ",") 219 | case *int: 220 | return strconv.FormatInt(int64(*p), 10) 221 | case *int8: 222 | return strconv.FormatInt(int64(*p), 10) 223 | case *int16: 224 | return strconv.FormatInt(int64(*p), 10) 225 | case *int32: 226 | return strconv.FormatInt(int64(*p), 10) 227 | case *int64: 228 | return strconv.FormatInt(*p, 10) 229 | case *uint: 230 | return strconv.FormatUint(uint64(*p), 10) 231 | case *uint8: 232 | return strconv.FormatUint(uint64(*p), 10) 233 | case *uint16: 234 | return strconv.FormatUint(uint64(*p), 10) 235 | case *uint32: 236 | return strconv.FormatUint(uint64(*p), 10) 237 | case *uint64: 238 | return strconv.FormatUint(*p, 10) 239 | case *float32: 240 | return strconv.FormatFloat(float64(*p), 'g', -1, 32) 241 | case *float64: 242 | return strconv.FormatFloat(*p, 'g', -1, 64) 243 | case *time.Duration: 244 | return p.String() 245 | } 246 | panic("internal error") 247 | } 248 | -------------------------------------------------------------------------------- /v2/generic_test.go: -------------------------------------------------------------------------------- 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 | 5 | package getopt 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "os" 11 | "reflect" 12 | "runtime" 13 | "strings" 14 | "testing" 15 | "time" 16 | ) 17 | 18 | func TestGeneric(t *testing.T) { 19 | const ( 20 | shortTest = iota 21 | longTest 22 | bothTest 23 | ) 24 | for _, tt := range []struct { 25 | where string 26 | kind int 27 | val interface{} 28 | str string 29 | def interface{} 30 | in []string 31 | err string 32 | }{ 33 | // Do all four tests for string, the rest can mostly just use 34 | // shortTest (the 0 value). 35 | { 36 | where: loc(), 37 | kind: shortTest, 38 | val: "42", 39 | str: "42", 40 | in: []string{"test", "-s", "42"}, 41 | }, 42 | { 43 | where: loc(), 44 | kind: longTest, 45 | val: "42", 46 | str: "42", 47 | in: []string{"test", "--long", "42"}, 48 | }, 49 | { 50 | where: loc(), 51 | kind: bothTest, 52 | val: "42", 53 | str: "42", 54 | in: []string{"test", "--both", "42"}, 55 | }, 56 | { 57 | where: loc(), 58 | kind: bothTest, 59 | val: "42", 60 | str: "42", 61 | in: []string{"test", "-b", "42"}, 62 | }, 63 | { 64 | where: loc(), 65 | val: "42", 66 | def: "42", 67 | str: "42", 68 | in: []string{"test"}, 69 | }, 70 | { 71 | where: loc(), 72 | val: "42", 73 | def: "43", 74 | str: "42", 75 | in: []string{"test", "-s", "42"}, 76 | }, 77 | 78 | { 79 | where: loc(), 80 | val: true, 81 | str: "true", 82 | in: []string{"test", "-s"}, 83 | }, 84 | { 85 | where: loc(), 86 | val: true, 87 | def: true, 88 | str: "true", 89 | in: []string{"test"}, 90 | }, 91 | { 92 | where: loc(), 93 | kind: longTest, 94 | val: false, 95 | str: "false", 96 | in: []string{"test", "--long=false"}, 97 | }, 98 | { 99 | where: loc(), 100 | kind: longTest, 101 | val: false, 102 | def: true, 103 | str: "false", 104 | in: []string{"test", "--long=false"}, 105 | }, 106 | 107 | { 108 | where: loc(), 109 | val: int(42), 110 | str: "42", 111 | in: []string{"test", "-s", "42"}, 112 | }, 113 | { 114 | where: loc(), 115 | val: int8(42), 116 | str: "42", 117 | in: []string{"test", "-s", "42"}, 118 | }, 119 | { 120 | where: loc(), 121 | val: int16(42), 122 | str: "42", 123 | in: []string{"test", "-s", "42"}, 124 | }, 125 | { 126 | where: loc(), 127 | val: int32(42), 128 | str: "42", 129 | in: []string{"test", "-s", "42"}, 130 | }, 131 | { 132 | where: loc(), 133 | val: int64(42), 134 | str: "42", 135 | in: []string{"test", "-s", "42"}, 136 | }, 137 | 138 | { 139 | where: loc(), 140 | val: uint(42), 141 | str: "42", 142 | in: []string{"test", "-s", "42"}, 143 | }, 144 | { 145 | where: loc(), 146 | val: uint8(42), 147 | str: "42", 148 | in: []string{"test", "-s", "42"}, 149 | }, 150 | { 151 | where: loc(), 152 | val: uint16(42), 153 | str: "42", 154 | in: []string{"test", "-s", "42"}, 155 | }, 156 | { 157 | where: loc(), 158 | val: uint32(42), 159 | str: "42", 160 | in: []string{"test", "-s", "42"}, 161 | }, 162 | { 163 | where: loc(), 164 | val: uint64(42), 165 | str: "42", 166 | in: []string{"test", "-s", "42"}, 167 | }, 168 | 169 | { 170 | where: loc(), 171 | val: float32(4.2), 172 | str: "4.2", 173 | in: []string{"test", "-s", "4.2"}, 174 | }, 175 | { 176 | where: loc(), 177 | val: float64(4.2), 178 | str: "4.2", 179 | in: []string{"test", "-s", "4.2"}, 180 | }, 181 | 182 | { 183 | where: loc(), 184 | val: time.Duration(time.Second * 42), 185 | def: time.Second * 2, 186 | str: "42s", 187 | in: []string{"test", "-s", "42s"}, 188 | }, 189 | { 190 | where: loc(), 191 | val: time.Duration(time.Second * 42), 192 | def: time.Second * 2, 193 | str: "42s", 194 | in: []string{"test", "-s42s"}, 195 | }, 196 | { 197 | where: loc(), 198 | val: time.Duration(time.Second * 2), 199 | def: time.Second * 2, 200 | in: []string{"test", "-s42"}, 201 | str: "2s", 202 | err: "test: time: missing unit in duration", 203 | }, 204 | 205 | { 206 | where: loc(), 207 | val: []string{"42", "."}, 208 | str: "42,.", 209 | def: []string{"one", "two", "three"}, 210 | in: []string{"test", "-s42", "-s."}, 211 | }, 212 | { 213 | where: loc(), 214 | val: []string{"42", "."}, 215 | str: "42,.", 216 | def: []string{"one", "two", "three"}, 217 | in: []string{"test", "-s42,."}, 218 | }, 219 | { 220 | where: loc(), 221 | val: []string{"one", "two", "three"}, 222 | def: []string{"one", "two", "three"}, 223 | str: "one,two,three", 224 | in: []string{"test"}, 225 | }, 226 | } { 227 | reset() 228 | var opt Option 229 | val := reflect.New(reflect.TypeOf(tt.val)).Interface() 230 | if tt.def != nil { 231 | reflect.ValueOf(val).Elem().Set(reflect.ValueOf(tt.def)) 232 | } 233 | switch tt.kind { 234 | case shortTest: 235 | opt = Flag(val, 's') 236 | case longTest: 237 | opt = FlagLong(val, "long", 0) 238 | case bothTest: 239 | opt = FlagLong(val, "both", 'b') 240 | } 241 | _ = opt 242 | parse(tt.in) 243 | if s := checkError(tt.err); s != "" { 244 | t.Errorf("%s: %s", tt.where, s) 245 | continue 246 | } 247 | got := reflect.ValueOf(val).Elem().Interface() 248 | want := reflect.ValueOf(tt.val).Interface() 249 | if !reflect.DeepEqual(got, want) { 250 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 251 | } 252 | if str := opt.String(); str != tt.str { 253 | t.Errorf("%s: got string %q, want %q", tt.where, str, tt.str) 254 | } 255 | } 256 | } 257 | 258 | func TestGenericDup(t *testing.T) { 259 | defer func() { 260 | stderr = os.Stderr 261 | exit = os.Exit 262 | }() 263 | 264 | reset() 265 | var v1, v2 string 266 | type myPanic struct{} 267 | var errbuf bytes.Buffer 268 | stderr = &errbuf 269 | _, file, line, _ := runtime.Caller(0) 270 | Flag(&v1, 's') 271 | line++ // line is now the line number of the first call to Flag. 272 | 273 | exit = func(i int) { panic(myPanic{}) } 274 | defer func() { 275 | p := recover() 276 | if _, ok := p.(myPanic); ok { 277 | err := errbuf.String() 278 | if !strings.Contains(err, "-s already declared") || !strings.Contains(err, fmt.Sprintf("%s:%d", file, line)) { 279 | t.Errorf("unexpected error: %q\nshould contain \"-s already declared\" and \"%s:%d\"", err, file, line) 280 | } 281 | } else if p == nil { 282 | t.Errorf("Second call to Flag did not fail") 283 | } else { 284 | t.Errorf("panic %v", p) 285 | } 286 | }() 287 | Flag(&v2, 's') 288 | } 289 | 290 | func TestGenericDupNested(t *testing.T) { 291 | defer func() { 292 | stderr = os.Stderr 293 | exit = os.Exit 294 | }() 295 | 296 | reset() 297 | type myPanic struct{} 298 | var errbuf bytes.Buffer 299 | stderr = &errbuf 300 | _, file, line, _ := runtime.Caller(0) 301 | String('s', "default") 302 | line++ // line is now the line number of the first call to Flag. 303 | 304 | exit = func(i int) { panic(myPanic{}) } 305 | defer func() { 306 | p := recover() 307 | if _, ok := p.(myPanic); ok { 308 | err := errbuf.String() 309 | if !strings.Contains(err, "-s already declared") || !strings.Contains(err, fmt.Sprintf("%s:%d", file, line)) { 310 | t.Errorf("unexpected error: %q\nshould contain \"-s already declared\" and \"%s:%d\"", err, file, line) 311 | } 312 | } else if p == nil { 313 | t.Errorf("Second call to Flag did not fail") 314 | } else { 315 | t.Errorf("panic %v", p) 316 | } 317 | }() 318 | String('s', "default") 319 | } 320 | -------------------------------------------------------------------------------- /v2/getopt_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 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 6 | 7 | import ( 8 | "testing" 9 | ) 10 | 11 | func TestMandatory(t *testing.T) { 12 | for _, tt := range []struct { 13 | name string 14 | in []string 15 | err string 16 | }{ 17 | { 18 | name: "required option present", 19 | in: []string{"test", "-r"}, 20 | }, 21 | { 22 | name: "required option not present", 23 | in: []string{"test", "-o"}, 24 | err: "test: option -r is mandatory", 25 | }, 26 | { 27 | name: "no options", 28 | in: []string{"test"}, 29 | err: "test: option -r is mandatory", 30 | }, 31 | } { 32 | reset() 33 | var val bool 34 | Flag(&val, 'o') 35 | Flag(&val, 'r').Mandatory() 36 | parse(tt.in) 37 | if s := checkError(tt.err); s != "" { 38 | t.Errorf("%s: %s", tt.name, s) 39 | } 40 | } 41 | } 42 | 43 | func TestGroup(t *testing.T) { 44 | for _, tt := range []struct { 45 | name string 46 | in []string 47 | err string 48 | }{ 49 | { 50 | name: "no args", 51 | in: []string{"test"}, 52 | err: "test: exactly one of the following options must be specified: -A, -B", 53 | }, 54 | { 55 | name: "one of each", 56 | in: []string{"test", "-A", "-C"}, 57 | }, 58 | { 59 | name: "Two in group One", 60 | in: []string{"test", "-A", "-B"}, 61 | err: "test: options -A and -B are mutually exclusive", 62 | }, 63 | { 64 | name: "Two in group Two", 65 | in: []string{"test", "-A", "-D", "-C"}, 66 | err: "test: options -C and -D are mutually exclusive", 67 | }, 68 | } { 69 | reset() 70 | var val bool 71 | Flag(&val, 'o') 72 | Flag(&val, 'A').SetGroup("One") 73 | Flag(&val, 'B').SetGroup("One") 74 | Flag(&val, 'C').SetGroup("Two") 75 | Flag(&val, 'D').SetGroup("Two") 76 | RequiredGroup("One") 77 | parse(tt.in) 78 | if s := checkError(tt.err); s != "" { 79 | t.Errorf("%s: %s", tt.name, s) 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /v2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/pborman/getopt/v2 2 | 3 | go 1.13 4 | -------------------------------------------------------------------------------- /v2/help_test.go: -------------------------------------------------------------------------------- 1 | package getopt 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "strings" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | type flagValue bool 12 | 13 | func (f *flagValue) Set(value string, opt Option) error { 14 | switch strings.ToLower(value) { 15 | case "true", "t", "on", "1": 16 | *f = true 17 | case "false", "f", "off", "0": 18 | *f = false 19 | default: 20 | return fmt.Errorf("invalid flagValue %q", value) 21 | } 22 | return nil 23 | } 24 | func (f *flagValue) String() string { 25 | return fmt.Sprint(bool(*f)) 26 | } 27 | 28 | func TestHelpDefaults(t *testing.T) { 29 | HelpColumn = 40 30 | set := New() 31 | bf := false 32 | bt := true 33 | set.FlagLong(&bf, "bool_false", 'f', "false bool value") 34 | set.FlagLong(&bt, "bool_true", 't', "true bool value") 35 | i := int(0) 36 | i8 := int8(0) 37 | i16 := int16(0) 38 | i32 := int32(0) 39 | i64 := int64(0) 40 | si := int(1) 41 | si8 := int8(8) 42 | si16 := int16(16) 43 | si32 := int32(32) 44 | si64 := int64(64) 45 | ui := uint(0) 46 | ui8 := uint8(0) 47 | ui16 := uint16(0) 48 | ui32 := uint32(0) 49 | ui64 := uint64(0) 50 | sui := uint(1) 51 | sui8 := uint8(8) 52 | sui16 := uint16(16) 53 | sui32 := uint32(32) 54 | sui64 := uint64(64) 55 | 56 | set.FlagLong(&i, "int", 0, "int value") 57 | set.FlagLong(&si, "int_set", 0, "set int value") 58 | set.FlagLong(&i8, "int8", 0, "int8 value") 59 | set.FlagLong(&si8, "int8_set", 0, "set int8 value") 60 | set.FlagLong(&i16, "int16", 0, "int16 value") 61 | set.FlagLong(&si16, "int16_set", 0, "set int16 value") 62 | set.FlagLong(&i32, "int32", 0, "int32 value") 63 | set.FlagLong(&si32, "int32_set", 0, "set int32 value") 64 | set.FlagLong(&i64, "int64", 0, "int64 value") 65 | set.FlagLong(&si64, "int64_set", 0, "set int64 value") 66 | 67 | set.FlagLong(&ui, "uint", 0, "uint value") 68 | set.FlagLong(&sui, "uint_set", 0, "set uint value") 69 | set.FlagLong(&ui8, "uint8", 0, "uint8 value") 70 | set.FlagLong(&sui8, "uint8_set", 0, "set uint8 value") 71 | set.FlagLong(&ui16, "uint16", 0, "uint16 value") 72 | set.FlagLong(&sui16, "uint16_set", 0, "set uint16 value") 73 | set.FlagLong(&ui32, "uint32", 0, "uint32 value") 74 | set.FlagLong(&sui32, "uint32_set", 0, "set uint32 value") 75 | set.FlagLong(&ui64, "uint64", 0, "uint64 value") 76 | set.FlagLong(&sui64, "uint64_set", 0, "set uint64 value") 77 | 78 | f32 := float32(0) 79 | f64 := float64(0) 80 | sf32 := float32(3.2) 81 | sf64 := float64(6.4) 82 | 83 | set.FlagLong(&f32, "float32", 0, "float32 value") 84 | set.FlagLong(&sf32, "float32_set", 0, "set float32 value") 85 | set.FlagLong(&f64, "float64", 0, "float64 value") 86 | set.FlagLong(&sf64, "float64_set", 0, "set float64 value") 87 | 88 | d := time.Duration(0) 89 | sd := time.Duration(time.Second) 90 | 91 | set.FlagLong(&d, "duration", 0, "duration value") 92 | set.FlagLong(&sd, "duration_set", 0, "set duration value") 93 | 94 | str := "" 95 | sstr := "string" 96 | set.FlagLong(&str, "string", 0, "string value") 97 | set.FlagLong(&sstr, "string_set", 0, "set string value") 98 | 99 | var fv flagValue 100 | set.FlagLong(&fv, "vbool", 0, "value bool").SetFlag() 101 | 102 | var fvo flagValue = true 103 | set.FlagLong(&fvo, "vbool_on", 0, "value bool").SetFlag() 104 | 105 | required := 17 106 | set.FlagLong(&required, "required", 0, "a required option").Mandatory() 107 | 108 | var a, b bool 109 | set.Flag(&a, 'a', "use method A").SetGroup("method") 110 | set.Flag(&b, 'b', "use method B").SetGroup("method") 111 | 112 | want := ` 113 | -a use method A {method} 114 | -b use method B {method} 115 | --duration=value duration value 116 | --duration_set=value set duration value [1s] 117 | -f, --bool_false false bool value 118 | --float32=value float32 value 119 | --float32_set=value set float32 value [3.2] 120 | --float64=value float64 value 121 | --float64_set=value set float64 value [6.4] 122 | --int=value int value 123 | --int16=value int16 value 124 | --int16_set=value set int16 value [16] 125 | --int32=value int32 value 126 | --int32_set=value set int32 value [32] 127 | --int64=value int64 value 128 | --int64_set=value set int64 value [64] 129 | --int8=value int8 value 130 | --int8_set=value set int8 value [8] 131 | --int_set=value set int value [1] 132 | --required=value a required option [17] (required) 133 | --string=value string value 134 | --string_set=value set string value [string] 135 | -t, --bool_true true bool value [true] 136 | --uint=value uint value 137 | --uint16=value uint16 value 138 | --uint16_set=value set uint16 value [16] 139 | --uint32=value uint32 value 140 | --uint32_set=value set uint32 value [32] 141 | --uint64=value uint64 value 142 | --uint64_set=value set uint64 value [64] 143 | --uint8=value uint8 value 144 | --uint8_set=value set uint8 value [8] 145 | --uint_set=value set uint value [1] 146 | --vbool value bool 147 | --vbool_on value bool [true] 148 | `[1:] 149 | 150 | var buf bytes.Buffer 151 | set.PrintOptions(&buf) 152 | if got := buf.String(); got != want { 153 | t.Errorf("got:\n%s\nwant:\n%s", got, want) 154 | } 155 | } 156 | 157 | func TestHelpString(t *testing.T) { 158 | 159 | set := New() 160 | 161 | descriptions := []string{ 162 | "test description", 163 | "", 164 | "💣💣", 165 | } 166 | 167 | testOption := func(t *testing.T, o Option, expected string) { 168 | if o == nil { 169 | t.Fatal("could not find option") 170 | } 171 | 172 | actual := o.Help() 173 | 174 | if actual != expected { 175 | t.Error("got", actual, "expected", expected) 176 | } 177 | } 178 | 179 | for i, expected := range descriptions { 180 | 181 | long := fmt.Sprintf("test%d", i) 182 | short := rune('a' + i) 183 | 184 | set.FlagLong(&i, long, short, expected) 185 | 186 | testOption(t, set.Lookup(long), expected) 187 | 188 | testOption(t, set.Lookup(short), expected) 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /v2/int.go: -------------------------------------------------------------------------------- 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 | 5 | package getopt 6 | 7 | // Int creates an option that parses its value as an integer. 8 | func Int(name rune, value int, helpvalue ...string) *int { 9 | return CommandLine.Int(name, value, helpvalue...) 10 | } 11 | 12 | func (s *Set) Int(name rune, value int, helpvalue ...string) *int { 13 | s.Flag(&value, name, helpvalue...) 14 | return &value 15 | } 16 | 17 | func IntLong(name string, short rune, value int, helpvalue ...string) *int { 18 | return CommandLine.IntLong(name, short, value, helpvalue...) 19 | } 20 | 21 | func (s *Set) IntLong(name string, short rune, value int, helpvalue ...string) *int { 22 | s.FlagLong(&value, name, short, helpvalue...) 23 | return &value 24 | } 25 | 26 | // Int16 creates an option that parses its value as a 16 bit integer. 27 | func Int16(name rune, value int16, helpvalue ...string) *int16 { 28 | return CommandLine.Int16(name, value, helpvalue...) 29 | } 30 | 31 | func (s *Set) Int16(name rune, value int16, helpvalue ...string) *int16 { 32 | s.Flag(&value, name, helpvalue...) 33 | return &value 34 | } 35 | 36 | func Int16Long(name string, short rune, value int16, helpvalue ...string) *int16 { 37 | return CommandLine.Int16Long(name, short, value, helpvalue...) 38 | } 39 | 40 | func (s *Set) Int16Long(name string, short rune, value int16, helpvalue ...string) *int16 { 41 | s.FlagLong(&value, name, short, helpvalue...) 42 | return &value 43 | } 44 | 45 | // Int32 creates an option that parses its value as a 32 bit integer. 46 | func Int32(name rune, value int32, helpvalue ...string) *int32 { 47 | return CommandLine.Int32(name, value, helpvalue...) 48 | } 49 | 50 | func (s *Set) Int32(name rune, value int32, helpvalue ...string) *int32 { 51 | s.Flag(&value, name, helpvalue...) 52 | return &value 53 | } 54 | 55 | func Int32Long(name string, short rune, value int32, helpvalue ...string) *int32 { 56 | return CommandLine.Int32Long(name, short, value, helpvalue...) 57 | } 58 | 59 | func (s *Set) Int32Long(name string, short rune, value int32, helpvalue ...string) *int32 { 60 | s.FlagLong(&value, name, short, helpvalue...) 61 | return &value 62 | } 63 | 64 | // Int64 creates an option that parses its value as a 64 bit integer. 65 | func Int64(name rune, value int64, helpvalue ...string) *int64 { 66 | return CommandLine.Int64(name, value, helpvalue...) 67 | } 68 | 69 | func (s *Set) Int64(name rune, value int64, helpvalue ...string) *int64 { 70 | s.Flag(&value, name, helpvalue...) 71 | return &value 72 | } 73 | 74 | func Int64Long(name string, short rune, value int64, helpvalue ...string) *int64 { 75 | return CommandLine.Int64Long(name, short, value, helpvalue...) 76 | } 77 | 78 | func (s *Set) Int64Long(name string, short rune, value int64, helpvalue ...string) *int64 { 79 | s.FlagLong(&value, name, short, helpvalue...) 80 | return &value 81 | } 82 | 83 | // Uint creates an option that parses its value as an unsigned integer. 84 | func Uint(name rune, value uint, helpvalue ...string) *uint { 85 | return CommandLine.Uint(name, value, helpvalue...) 86 | } 87 | 88 | func (s *Set) Uint(name rune, value uint, helpvalue ...string) *uint { 89 | s.Flag(&value, name, helpvalue...) 90 | return &value 91 | } 92 | 93 | func UintLong(name string, short rune, value uint, helpvalue ...string) *uint { 94 | return CommandLine.UintLong(name, short, value, helpvalue...) 95 | } 96 | 97 | func (s *Set) UintLong(name string, short rune, value uint, helpvalue ...string) *uint { 98 | s.FlagLong(&value, name, short, helpvalue...) 99 | return &value 100 | } 101 | 102 | // Uint16 creates an option that parses its value as a 16 bit unsigned integer. 103 | func Uint16(name rune, value uint16, helpvalue ...string) *uint16 { 104 | return CommandLine.Uint16(name, value, helpvalue...) 105 | } 106 | 107 | func (s *Set) Uint16(name rune, value uint16, helpvalue ...string) *uint16 { 108 | s.Flag(&value, name, helpvalue...) 109 | return &value 110 | } 111 | 112 | func Uint16Long(name string, short rune, value uint16, helpvalue ...string) *uint16 { 113 | return CommandLine.Uint16Long(name, short, value, helpvalue...) 114 | } 115 | 116 | func (s *Set) Uint16Long(name string, short rune, value uint16, helpvalue ...string) *uint16 { 117 | s.FlagLong(&value, name, short, helpvalue...) 118 | return &value 119 | } 120 | 121 | // Uint32 creates an option that parses its value as a 32 bit unsigned integer. 122 | func Uint32(name rune, value uint32, helpvalue ...string) *uint32 { 123 | return CommandLine.Uint32(name, value, helpvalue...) 124 | } 125 | 126 | func (s *Set) Uint32(name rune, value uint32, helpvalue ...string) *uint32 { 127 | s.Flag(&value, name, helpvalue...) 128 | return &value 129 | } 130 | 131 | func Uint32Long(name string, short rune, value uint32, helpvalue ...string) *uint32 { 132 | return CommandLine.Uint32Long(name, short, value, helpvalue...) 133 | } 134 | 135 | func (s *Set) Uint32Long(name string, short rune, value uint32, helpvalue ...string) *uint32 { 136 | s.FlagLong(&value, name, short, helpvalue...) 137 | return &value 138 | } 139 | 140 | // Uint64 creates an option that parses its value as a 64 bit unsigned integer. 141 | func Uint64(name rune, value uint64, helpvalue ...string) *uint64 { 142 | return CommandLine.Uint64(name, value, helpvalue...) 143 | } 144 | 145 | func (s *Set) Uint64(name rune, value uint64, helpvalue ...string) *uint64 { 146 | s.Flag(&value, name, helpvalue...) 147 | return &value 148 | } 149 | 150 | func Uint64Long(name string, short rune, value uint64, helpvalue ...string) *uint64 { 151 | return CommandLine.Uint64Long(name, short, value, helpvalue...) 152 | } 153 | 154 | func (s *Set) Uint64Long(name string, short rune, value uint64, helpvalue ...string) *uint64 { 155 | s.FlagLong(&value, name, short, helpvalue...) 156 | return &value 157 | } 158 | -------------------------------------------------------------------------------- /v2/int_test.go: -------------------------------------------------------------------------------- 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 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var intTests = []struct { 14 | where string 15 | in []string 16 | i int 17 | int int 18 | err string 19 | }{ 20 | { 21 | loc(), 22 | []string{}, 23 | 17, 42, 24 | "", 25 | }, 26 | { 27 | loc(), 28 | []string{"test", "-i", "1", "--int", "2"}, 29 | 1, 2, 30 | "", 31 | }, 32 | { 33 | loc(), 34 | []string{"test", "-i1", "--int=2"}, 35 | 1, 2, 36 | "", 37 | }, 38 | { 39 | loc(), 40 | []string{"test", "-i1", "-i2"}, 41 | 2, 42, 42 | "", 43 | }, 44 | { 45 | loc(), 46 | []string{"test", "-i=1"}, 47 | 17, 42, 48 | "test: not a valid number: =1\n", 49 | }, 50 | { 51 | loc(), 52 | []string{"test", "-i0x20"}, 53 | 0x20, 42, 54 | "", 55 | }, 56 | { 57 | loc(), 58 | []string{"test", "-i010"}, 59 | 8, 42, 60 | "", 61 | }, 62 | } 63 | 64 | func TestInt(t *testing.T) { 65 | for x, tt := range intTests { 66 | reset() 67 | i := Int('i', 17) 68 | opt := IntLong("int", 0, 42) 69 | if strings.Index(tt.where, ":-") > 0 { 70 | tt.where = fmt.Sprintf("#%d", x) 71 | } 72 | 73 | parse(tt.in) 74 | if s := checkError(tt.err); s != "" { 75 | t.Errorf("%s: %s", tt.where, s) 76 | } 77 | if got, want := *i, tt.i; got != want { 78 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 79 | } 80 | if got, want := *opt, tt.int; got != want { 81 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 82 | } 83 | } 84 | } 85 | 86 | var int16Tests = []struct { 87 | where string 88 | in []string 89 | i int16 90 | int16 int16 91 | err string 92 | }{ 93 | { 94 | loc(), 95 | []string{}, 96 | 17, 42, 97 | "", 98 | }, 99 | { 100 | loc(), 101 | []string{"test", "-i", "1", "--int16", "2"}, 102 | 1, 2, 103 | "", 104 | }, 105 | { 106 | loc(), 107 | []string{"test", "-i1", "--int16=2"}, 108 | 1, 2, 109 | "", 110 | }, 111 | { 112 | loc(), 113 | []string{"test", "-i1", "-i2"}, 114 | 2, 42, 115 | "", 116 | }, 117 | { 118 | loc(), 119 | []string{"test", "-i=1"}, 120 | 17, 42, 121 | "test: not a valid number: =1\n", 122 | }, 123 | { 124 | loc(), 125 | []string{"test", "-i0x20"}, 126 | 0x20, 42, 127 | "", 128 | }, 129 | { 130 | loc(), 131 | []string{"test", "-i010"}, 132 | 8, 42, 133 | "", 134 | }, 135 | } 136 | 137 | func TestInt16(t *testing.T) { 138 | for x, tt := range int16Tests { 139 | reset() 140 | i := Int16('i', 17) 141 | opt := Int16Long("int16", 0, 42) 142 | if strings.Index(tt.where, ":-") > 0 { 143 | tt.where = fmt.Sprintf("#%d", x) 144 | } 145 | 146 | parse(tt.in) 147 | if s := checkError(tt.err); s != "" { 148 | t.Errorf("%s: %s", tt.where, s) 149 | } 150 | if got, want := *i, tt.i; got != want { 151 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 152 | } 153 | if got, want := *opt, tt.int16; got != want { 154 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 155 | } 156 | } 157 | } 158 | 159 | var int32Tests = []struct { 160 | where string 161 | in []string 162 | i int32 163 | int32 int32 164 | err string 165 | }{ 166 | { 167 | loc(), 168 | []string{}, 169 | 17, 42, 170 | "", 171 | }, 172 | { 173 | loc(), 174 | []string{"test", "-i", "1", "--int32", "2"}, 175 | 1, 2, 176 | "", 177 | }, 178 | { 179 | loc(), 180 | []string{"test", "-i1", "--int32=2"}, 181 | 1, 2, 182 | "", 183 | }, 184 | { 185 | loc(), 186 | []string{"test", "-i1", "-i2"}, 187 | 2, 42, 188 | "", 189 | }, 190 | { 191 | loc(), 192 | []string{"test", "-i=1"}, 193 | 17, 42, 194 | "test: not a valid number: =1\n", 195 | }, 196 | { 197 | loc(), 198 | []string{"test", "-i0x20"}, 199 | 0x20, 42, 200 | "", 201 | }, 202 | { 203 | loc(), 204 | []string{"test", "-i010"}, 205 | 8, 42, 206 | "", 207 | }, 208 | } 209 | 210 | func TestInt32(t *testing.T) { 211 | for x, tt := range int32Tests { 212 | reset() 213 | i := Int32('i', 17) 214 | opt := Int32Long("int32", 0, 42) 215 | if strings.Index(tt.where, ":-") > 0 { 216 | tt.where = fmt.Sprintf("#%d", x) 217 | } 218 | 219 | parse(tt.in) 220 | if s := checkError(tt.err); s != "" { 221 | t.Errorf("%s: %s", tt.where, s) 222 | } 223 | if got, want := *i, tt.i; got != want { 224 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 225 | } 226 | if got, want := *opt, tt.int32; got != want { 227 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 228 | } 229 | } 230 | } 231 | 232 | var int64Tests = []struct { 233 | where string 234 | in []string 235 | i int64 236 | int64 int64 237 | err string 238 | }{ 239 | { 240 | loc(), 241 | []string{}, 242 | 17, 42, 243 | "", 244 | }, 245 | { 246 | loc(), 247 | []string{"test", "-i", "1", "--int64", "2"}, 248 | 1, 2, 249 | "", 250 | }, 251 | { 252 | loc(), 253 | []string{"test", "-i1", "--int64=2"}, 254 | 1, 2, 255 | "", 256 | }, 257 | { 258 | loc(), 259 | []string{"test", "-i1", "-i2"}, 260 | 2, 42, 261 | "", 262 | }, 263 | { 264 | loc(), 265 | []string{"test", "-i=1"}, 266 | 17, 42, 267 | "test: not a valid number: =1\n", 268 | }, 269 | { 270 | loc(), 271 | []string{"test", "-i0x20"}, 272 | 0x20, 42, 273 | "", 274 | }, 275 | { 276 | loc(), 277 | []string{"test", "-i010"}, 278 | 8, 42, 279 | "", 280 | }, 281 | } 282 | 283 | func TestInt64(t *testing.T) { 284 | for x, tt := range int64Tests { 285 | reset() 286 | i := Int64('i', 17) 287 | opt := Int64Long("int64", 0, 42) 288 | if strings.Index(tt.where, ":-") > 0 { 289 | tt.where = fmt.Sprintf("#%d", x) 290 | } 291 | 292 | parse(tt.in) 293 | if s := checkError(tt.err); s != "" { 294 | t.Errorf("%s: %s", tt.where, s) 295 | } 296 | if got, want := *i, tt.i; got != want { 297 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 298 | } 299 | if got, want := *opt, tt.int64; got != want { 300 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 301 | } 302 | } 303 | } 304 | 305 | var uintTests = []struct { 306 | where string 307 | in []string 308 | i uint 309 | uint uint 310 | err string 311 | }{ 312 | { 313 | loc(), 314 | []string{}, 315 | 17, 42, 316 | "", 317 | }, 318 | { 319 | loc(), 320 | []string{"test", "-i", "1", "--uint", "2"}, 321 | 1, 2, 322 | "", 323 | }, 324 | { 325 | loc(), 326 | []string{"test", "-i1", "--uint=2"}, 327 | 1, 2, 328 | "", 329 | }, 330 | { 331 | loc(), 332 | []string{"test", "-i1", "-i2"}, 333 | 2, 42, 334 | "", 335 | }, 336 | { 337 | loc(), 338 | []string{"test", "-i=1"}, 339 | 17, 42, 340 | "test: not a valid number: =1\n", 341 | }, 342 | { 343 | loc(), 344 | []string{"test", "-i0x20"}, 345 | 0x20, 42, 346 | "", 347 | }, 348 | { 349 | loc(), 350 | []string{"test", "-i010"}, 351 | 8, 42, 352 | "", 353 | }, 354 | } 355 | 356 | func TestUint(t *testing.T) { 357 | for x, tt := range uintTests { 358 | reset() 359 | i := Uint('i', 17) 360 | opt := UintLong("uint", 0, 42) 361 | if strings.Index(tt.where, ":-") > 0 { 362 | tt.where = fmt.Sprintf("#%d", x) 363 | } 364 | 365 | parse(tt.in) 366 | if s := checkError(tt.err); s != "" { 367 | t.Errorf("%s: %s", tt.where, s) 368 | } 369 | if got, want := *i, tt.i; got != want { 370 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 371 | } 372 | if got, want := *opt, tt.uint; got != want { 373 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 374 | } 375 | } 376 | } 377 | 378 | var uint16Tests = []struct { 379 | where string 380 | in []string 381 | i uint16 382 | uint16 uint16 383 | err string 384 | }{ 385 | { 386 | loc(), 387 | []string{}, 388 | 17, 42, 389 | "", 390 | }, 391 | { 392 | loc(), 393 | []string{"test", "-i", "1", "--uint16", "2"}, 394 | 1, 2, 395 | "", 396 | }, 397 | { 398 | loc(), 399 | []string{"test", "-i1", "--uint16=2"}, 400 | 1, 2, 401 | "", 402 | }, 403 | { 404 | loc(), 405 | []string{"test", "-i1", "-i2"}, 406 | 2, 42, 407 | "", 408 | }, 409 | { 410 | loc(), 411 | []string{"test", "-i=1"}, 412 | 17, 42, 413 | "test: not a valid number: =1\n", 414 | }, 415 | { 416 | loc(), 417 | []string{"test", "-i0x20"}, 418 | 0x20, 42, 419 | "", 420 | }, 421 | { 422 | loc(), 423 | []string{"test", "-i010"}, 424 | 8, 42, 425 | "", 426 | }, 427 | } 428 | 429 | func TestUint16(t *testing.T) { 430 | for x, tt := range uint16Tests { 431 | reset() 432 | i := Uint16('i', 17) 433 | opt := Uint16Long("uint16", 0, 42) 434 | if strings.Index(tt.where, ":-") > 0 { 435 | tt.where = fmt.Sprintf("#%d", x) 436 | } 437 | 438 | parse(tt.in) 439 | if s := checkError(tt.err); s != "" { 440 | t.Errorf("%s: %s", tt.where, s) 441 | } 442 | if got, want := *i, tt.i; got != want { 443 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 444 | } 445 | if got, want := *opt, tt.uint16; got != want { 446 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 447 | } 448 | } 449 | } 450 | 451 | var uint32Tests = []struct { 452 | where string 453 | in []string 454 | i uint32 455 | uint32 uint32 456 | err string 457 | }{ 458 | { 459 | loc(), 460 | []string{}, 461 | 17, 42, 462 | "", 463 | }, 464 | { 465 | loc(), 466 | []string{"test", "-i", "1", "--uint32", "2"}, 467 | 1, 2, 468 | "", 469 | }, 470 | { 471 | loc(), 472 | []string{"test", "-i1", "--uint32=2"}, 473 | 1, 2, 474 | "", 475 | }, 476 | { 477 | loc(), 478 | []string{"test", "-i1", "-i2"}, 479 | 2, 42, 480 | "", 481 | }, 482 | { 483 | loc(), 484 | []string{"test", "-i=1"}, 485 | 17, 42, 486 | "test: not a valid number: =1\n", 487 | }, 488 | { 489 | loc(), 490 | []string{"test", "-i0x20"}, 491 | 0x20, 42, 492 | "", 493 | }, 494 | { 495 | loc(), 496 | []string{"test", "-i010"}, 497 | 8, 42, 498 | "", 499 | }, 500 | } 501 | 502 | func TestUint32(t *testing.T) { 503 | for x, tt := range uint32Tests { 504 | reset() 505 | i := Uint32('i', 17) 506 | opt := Uint32Long("uint32", 0, 42) 507 | if strings.Index(tt.where, ":-") > 0 { 508 | tt.where = fmt.Sprintf("#%d", x) 509 | } 510 | 511 | parse(tt.in) 512 | if s := checkError(tt.err); s != "" { 513 | t.Errorf("%s: %s", tt.where, s) 514 | } 515 | if got, want := *i, tt.i; got != want { 516 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 517 | } 518 | if got, want := *opt, tt.uint32; got != want { 519 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 520 | } 521 | } 522 | } 523 | 524 | var uint64Tests = []struct { 525 | where string 526 | in []string 527 | i uint64 528 | uint64 uint64 529 | err string 530 | }{ 531 | { 532 | loc(), 533 | []string{}, 534 | 17, 42, 535 | "", 536 | }, 537 | { 538 | loc(), 539 | []string{"test", "-i", "1", "--uint64", "2"}, 540 | 1, 2, 541 | "", 542 | }, 543 | { 544 | loc(), 545 | []string{"test", "-i1", "--uint64=2"}, 546 | 1, 2, 547 | "", 548 | }, 549 | { 550 | loc(), 551 | []string{"test", "-i1", "-i2"}, 552 | 2, 42, 553 | "", 554 | }, 555 | { 556 | loc(), 557 | []string{"test", "-i=1"}, 558 | 17, 42, 559 | "test: not a valid number: =1\n", 560 | }, 561 | { 562 | loc(), 563 | []string{"test", "-i0x20"}, 564 | 0x20, 42, 565 | "", 566 | }, 567 | { 568 | loc(), 569 | []string{"test", "-i010"}, 570 | 8, 42, 571 | "", 572 | }, 573 | } 574 | 575 | func TestUint64(t *testing.T) { 576 | for x, tt := range uint64Tests { 577 | reset() 578 | i := Uint64('i', 17) 579 | opt := Uint64Long("uint64", 0, 42) 580 | if strings.Index(tt.where, ":-") > 0 { 581 | tt.where = fmt.Sprintf("#%d", x) 582 | } 583 | 584 | parse(tt.in) 585 | if s := checkError(tt.err); s != "" { 586 | t.Errorf("%s: %s", tt.where, s) 587 | } 588 | if got, want := *i, tt.i; got != want { 589 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 590 | } 591 | if got, want := *opt, tt.uint64; got != want { 592 | t.Errorf("%s: got %v, want %v", tt.where, got, want) 593 | } 594 | } 595 | } 596 | -------------------------------------------------------------------------------- /v2/list.go: -------------------------------------------------------------------------------- 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 | 5 | package getopt 6 | 7 | // List creates an option that returns a slice of strings. The parameters 8 | // passed are converted from a comma separated value list into a slice. 9 | // Subsequent occurrences append to the list. 10 | func List(name rune, helpvalue ...string) *[]string { 11 | p := []string{} 12 | CommandLine.Flag(&p, name, helpvalue...) 13 | return &p 14 | } 15 | 16 | func (s *Set) List(name rune, helpvalue ...string) *[]string { 17 | p := []string{} 18 | s.Flag(&p, name, helpvalue...) 19 | return &p 20 | } 21 | 22 | func ListLong(name string, short rune, helpvalue ...string) *[]string { 23 | p := []string{} 24 | CommandLine.FlagLong(&p, name, short, helpvalue...) 25 | return &p 26 | } 27 | 28 | func (s *Set) ListLong(name string, short rune, helpvalue ...string) *[]string { 29 | p := []string{} 30 | s.FlagLong(&p, name, short, helpvalue...) 31 | return &p 32 | } 33 | -------------------------------------------------------------------------------- /v2/list_test.go: -------------------------------------------------------------------------------- 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 | 5 | package getopt 6 | 7 | import "testing" 8 | 9 | var listTests = []struct { 10 | where string 11 | in []string 12 | l, list []string 13 | err string 14 | }{ 15 | { 16 | loc(), 17 | []string{}, 18 | nil, nil, 19 | "", 20 | }, 21 | { 22 | loc(), 23 | []string{"test", "-l", "one"}, 24 | []string{"one"}, nil, 25 | "", 26 | }, 27 | { 28 | loc(), 29 | []string{"test", "-lone", "-ltwo"}, 30 | []string{"one", "two"}, nil, 31 | "", 32 | }, 33 | { 34 | loc(), 35 | []string{"test", "--list", "one"}, 36 | nil, []string{"one"}, 37 | "", 38 | }, 39 | { 40 | loc(), 41 | []string{"test", "--list=one", "--list=two"}, 42 | nil, []string{"one", "two"}, 43 | "", 44 | }, 45 | { 46 | loc(), 47 | []string{"test", "--list=one,two"}, 48 | nil, []string{"one", "two"}, 49 | "", 50 | }, 51 | } 52 | 53 | func TestList(t *testing.T) { 54 | for _, tt := range listTests { 55 | reset() 56 | l := List('l') 57 | list := ListLong("list", 0) 58 | 59 | parse(tt.in) 60 | if s := checkError(tt.err); s != "" { 61 | t.Errorf("%s: %s", tt.where, s) 62 | } 63 | if badSlice(*l, tt.l) { 64 | t.Errorf("%s: got s = %q, want %q", tt.where, *l, tt.l) 65 | } 66 | if badSlice(*list, tt.list) { 67 | t.Errorf("%s: got s = %q, want %q", tt.where, *list, tt.list) 68 | } 69 | } 70 | } 71 | 72 | func TestDefaultList(t *testing.T) { 73 | reset() 74 | list := []string{"d1", "d2", "d3"} 75 | Flag(&list, 'l') 76 | parse([]string{"test"}) 77 | 78 | want := []string{"d1", "d2", "d3"} 79 | if badSlice(list, want) { 80 | t.Errorf("got s = %q, want %q", list, want) 81 | } 82 | 83 | parse([]string{"test", "-l", "one"}) 84 | want = []string{"one"} 85 | if badSlice(list, want) { 86 | t.Errorf("got s = %q, want %q", list, want) 87 | } 88 | 89 | parse([]string{"test", "-l", "two"}) 90 | want = []string{"one", "two"} 91 | if badSlice(list, want) { 92 | t.Errorf("got s = %q, want %q", list, want) 93 | } 94 | Lookup('l').Reset() 95 | want = []string{"d1", "d2", "d3"} 96 | if badSlice(list, want) { 97 | t.Errorf("got s = %q, want %q", list, want) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /v2/option.go: -------------------------------------------------------------------------------- 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 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | ) 11 | 12 | // An Option can be either a Flag or a Value 13 | type 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 | // ShortName always returns the short name of the option, or "" if there 21 | // is no short name. The name does not include the "-". 22 | ShortName() string 23 | 24 | // LongName always returns the long name of the option, or "" if there 25 | // is no long name. The name does not include the "--". 26 | LongName() string 27 | 28 | // Help always returns a description of the option, or "" if there 29 | // is no description. 30 | Help() string 31 | 32 | // IsFlag returns true if Option is a flag. 33 | IsFlag() bool 34 | 35 | // Seen returns true if the flag was seen. 36 | Seen() bool 37 | 38 | // Count returns the number of times the flag was seen. 39 | Count() int 40 | 41 | // String returns the last value the option was set to. 42 | String() string 43 | 44 | // Value returns the Value of the option. 45 | Value() Value 46 | 47 | // SetOptional makes the value optional. The option and value are 48 | // always a single argument. Either --option or --option=value. In 49 | // the former case the value of the option does not change but the Set() 50 | // will return true and the value returned by Count() is incremented. 51 | // The short form is either -o or -ovalue. SetOptional returns 52 | // the Option 53 | SetOptional() Option 54 | 55 | // SetFlag makes the value a flag. Flags are boolean values and 56 | // normally do not taken a value. They are set to true when seen. 57 | // If a value is passed in the long form then it must be on, case 58 | // insensitivinsensitive, one of "true", "false", "t", "f", "on", "off", "1", "0". 59 | // SetFlag returns the Option 60 | SetFlag() Option 61 | 62 | // Reset resets the state of the option so it appears it has not 63 | // yet been seen, including resetting the value of the option 64 | // to its original default state. 65 | Reset() 66 | 67 | // Mandataory sets the mandatory flag of the option. Parse will 68 | // fail if a mandatory option is missing. 69 | Mandatory() Option 70 | 71 | // SetGroup sets the option as part of a radio group. Parse will 72 | // fail if two options in the same group are seen. 73 | SetGroup(string) Option 74 | } 75 | 76 | type option struct { 77 | short rune // 0 means no short name 78 | long string // "" means no long name 79 | isLong bool // True if they used the long name 80 | flag bool // true if a boolean flag 81 | defval string // default value 82 | optional bool // true if we take an optional value 83 | help string // help message 84 | where string // file where the option was defined 85 | value Value // current value of option 86 | count int // number of times we have seen this option 87 | name string // name of the value (for usage) 88 | uname string // name of the option (for usage) 89 | mandatory bool // this option must be specified 90 | group string // mutual exclusion group 91 | } 92 | 93 | // usageName returns the name of the option for printing usage lines in one 94 | // of the following forms: 95 | // 96 | // -f 97 | // --flag 98 | // -f, --flag 99 | // -s value 100 | // --set=value 101 | // -s, --set=value 102 | func (o *option) usageName() string { 103 | // Don't print help messages if we have none and there is only one 104 | // way to specify the option. 105 | if o.help == "" && (o.short == 0 || o.long == "") { 106 | return "" 107 | } 108 | n := "" 109 | 110 | switch { 111 | case o.short != 0 && o.long == "": 112 | n = "-" + string(o.short) 113 | case o.short == 0 && o.long != "": 114 | n = " --" + o.long 115 | case o.short != 0 && o.long != "": 116 | n = "-" + string(o.short) + ", --" + o.long 117 | } 118 | 119 | switch { 120 | case o.flag: 121 | return n 122 | case o.optional: 123 | return n + "[=" + o.name + "]" 124 | case o.long != "": 125 | return n + "=" + o.name 126 | } 127 | return n + " " + o.name 128 | } 129 | 130 | // sortName returns the name to sort the option on. 131 | func (o *option) sortName() string { 132 | if o.short != 0 { 133 | return string(o.short) + o.long 134 | } 135 | return o.long[:1] + o.long 136 | } 137 | 138 | func (o *option) Seen() bool { return o.count > 0 } 139 | func (o *option) Count() int { return o.count } 140 | func (o *option) IsFlag() bool { return o.flag } 141 | func (o *option) String() string { return o.value.String() } 142 | func (o *option) SetOptional() Option { o.optional = true; return o } 143 | func (o *option) SetFlag() Option { o.flag = true; return o } 144 | func (o *option) Mandatory() Option { o.mandatory = true; return o } 145 | func (o *option) SetGroup(g string) Option { o.group = g; return o } 146 | 147 | func (o *option) Value() Value { 148 | if o == nil { 149 | return nil 150 | } 151 | return o.value 152 | } 153 | 154 | func (o *option) Name() string { 155 | if !o.isLong && o.short != 0 { 156 | return "-" + string(o.short) 157 | } 158 | return "--" + o.long 159 | } 160 | 161 | func (o *option) ShortName() string { 162 | if o.short != 0 { 163 | return string(o.short) 164 | } 165 | return "" 166 | } 167 | 168 | func (o *option) LongName() string { 169 | return o.long 170 | } 171 | 172 | func (o *option) Help() string { 173 | return o.help 174 | } 175 | 176 | // Reset rests an option so that it appears it has not yet been seen. 177 | func (o *option) Reset() { 178 | o.isLong = false 179 | o.count = 0 180 | o.value.Set(o.defval, o) 181 | } 182 | 183 | type optionList []*option 184 | 185 | func (ol optionList) Len() int { return len(ol) } 186 | func (ol optionList) Swap(i, j int) { ol[i], ol[j] = ol[j], ol[i] } 187 | func (ol optionList) Less(i, j int) bool { 188 | // first check the short names (or the first letter of the long name) 189 | // If they are not equal (case insensitive) then we have our answer 190 | n1 := ol[i].sortName() 191 | n2 := ol[j].sortName() 192 | l1 := strings.ToLower(n1) 193 | l2 := strings.ToLower(n2) 194 | if l1 < l2 { 195 | return true 196 | } 197 | if l2 < l1 { 198 | return false 199 | } 200 | return n1 < n2 201 | } 202 | 203 | // AddOption add the option o to set CommandLine if o is not already in set 204 | // CommandLine. 205 | func AddOption(o Option) { 206 | CommandLine.AddOption(o) 207 | } 208 | 209 | // AddOption add the option o to set s if o is not already in set s. 210 | func (s *Set) AddOption(o Option) { 211 | opt := o.(*option) 212 | for _, eopt := range s.options { 213 | if opt == eopt { 214 | return 215 | } 216 | } 217 | if opt.short != 0 { 218 | if oo, ok := s.shortOptions[opt.short]; ok { 219 | fmt.Fprintf(stderr, "%s: -%c already declared at %s\n", opt.where, opt.short, oo.where) 220 | exit(1) 221 | } 222 | s.shortOptions[opt.short] = opt 223 | } 224 | if opt.long != "" { 225 | if oo, ok := s.longOptions[opt.long]; ok { 226 | fmt.Fprintf(stderr, "%s: --%s already declared at %s\n", opt.where, opt.long, oo.where) 227 | exit(1) 228 | } 229 | s.longOptions[opt.long] = opt 230 | } 231 | s.options = append(s.options, opt) 232 | } 233 | -------------------------------------------------------------------------------- /v2/set.go: -------------------------------------------------------------------------------- 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 | 5 | package getopt 6 | 7 | import ( 8 | "io" 9 | "os" 10 | "sort" 11 | "sync" 12 | ) 13 | 14 | // A State is why the Getopt returned. 15 | type State int 16 | 17 | const ( 18 | InProgress = State(iota) // Getopt is still running 19 | Dash // Returned on "-" 20 | DashDash // Returned on "--" 21 | EndOfOptions // End of options reached 22 | EndOfArguments // No more arguments 23 | Terminated // Terminated by callback function 24 | Failure // Terminated due to error 25 | Unknown // Indicates internal error 26 | ) 27 | 28 | type Set struct { 29 | stateMu sync.Mutex 30 | state State 31 | 32 | // args are the parameters remaining after parsing the optoins. 33 | args []string 34 | 35 | // program is the name of the program for usage and error messages. 36 | // If not set it will automatically be set to the base name of the 37 | // first argument passed to parse. 38 | program string 39 | 40 | // parameters is what is displayed on the usage line after displaying 41 | // the various options. 42 | parameters string 43 | 44 | usage func() // usage should print the programs usage and exit. 45 | 46 | shortOptions map[rune]*option 47 | longOptions map[string]*option 48 | options optionList 49 | requiredGroups []string 50 | } 51 | 52 | // New returns a newly created option set. 53 | func New() *Set { 54 | s := &Set{ 55 | shortOptions: make(map[rune]*option), 56 | longOptions: make(map[string]*option), 57 | parameters: "[parameters ...]", 58 | } 59 | 60 | s.usage = func() { 61 | s.PrintUsage(stderr) 62 | } 63 | return s 64 | } 65 | 66 | func (s *Set) setState(state State) { 67 | s.stateMu.Lock() 68 | s.state = state 69 | s.stateMu.Unlock() 70 | } 71 | 72 | // State returns the current state of the Set s. The state is normally the 73 | // reason the most recent call to Getopt returned. 74 | func (s *Set) State() State { 75 | s.stateMu.Lock() 76 | defer s.stateMu.Unlock() 77 | return s.state 78 | } 79 | 80 | // The default set of command-line options. 81 | var CommandLine = New() 82 | 83 | // PrintUsage calls PrintUsage in the default option set. 84 | func PrintUsage(w io.Writer) { CommandLine.PrintUsage(w) } 85 | 86 | // Usage calls the usage function in the default option set. 87 | func Usage() { CommandLine.usage() } 88 | 89 | // Parse calls Parse in the default option set with the command line arguments 90 | // found in os.Args. 91 | func Parse() { CommandLine.Parse(os.Args) } 92 | 93 | // Same as parse but not found in version 1 of getopt. 94 | func ParseV2() { CommandLine.Parse(os.Args) } 95 | 96 | // Getops returns the result of calling Getop in the default option set with the 97 | // command line arguments found in os.Args. The fn function, which may be nil, 98 | // is passed to Getopt. 99 | func Getopt(fn func(Option) bool) error { return CommandLine.Getopt(os.Args, fn) } 100 | 101 | // Arg returns the n'th command-line argument. Arg(0) is the first remaining 102 | // argument after options have been processed. 103 | func Arg(n int) string { 104 | if n >= 0 && n < len(CommandLine.args) { 105 | return CommandLine.args[n] 106 | } 107 | return "" 108 | } 109 | 110 | // Arg returns the n'th argument. Arg(0) is the first remaining 111 | // argument after options have been processed. 112 | func (s *Set) Arg(n int) string { 113 | if n >= 0 && n < len(s.args) { 114 | return s.args[n] 115 | } 116 | return "" 117 | } 118 | 119 | // Args returns the non-option command line arguments. 120 | func Args() []string { 121 | return CommandLine.args 122 | } 123 | 124 | // Args returns the non-option arguments. 125 | func (s *Set) Args() []string { 126 | return s.args 127 | } 128 | 129 | // NArgs returns the number of non-option command line arguments. 130 | func NArgs() int { 131 | return len(CommandLine.args) 132 | } 133 | 134 | // NArgs returns the number of non-option arguments. 135 | func (s *Set) NArgs() int { 136 | return len(s.args) 137 | } 138 | 139 | // SetParameters sets the parameters string for printing the command line 140 | // usage. It defaults to "[parameters ...]" 141 | func SetParameters(parameters string) { 142 | CommandLine.parameters = parameters 143 | } 144 | 145 | // SetParameters sets the parameters string for printing the s's usage. 146 | // It defaults to "[parameters ...]" 147 | func (s *Set) SetParameters(parameters string) { 148 | s.parameters = parameters 149 | } 150 | 151 | // Parameters returns the parameters set by SetParameters on s. 152 | func (s *Set) Parameters() string { return s.parameters } 153 | 154 | // SetProgram sets the program name to program. Normally it is determined 155 | // from the zeroth command line argument (see os.Args). 156 | func SetProgram(program string) { 157 | CommandLine.program = program 158 | } 159 | 160 | // SetProgram sets s's program name to program. Normally it is determined 161 | // from the zeroth argument passed to Getopt or Parse. 162 | func (s *Set) SetProgram(program string) { 163 | s.program = program 164 | } 165 | 166 | // Program returns the program name associated with Set s. 167 | func (s *Set) Program() string { return s.program } 168 | 169 | // SetUsage sets the function used by Parse to display the commands usage 170 | // on error. It defaults to calling PrintUsage(os.Stderr). 171 | func SetUsage(usage func()) { 172 | CommandLine.usage = usage 173 | } 174 | 175 | // SetUsage sets the function used by Parse to display usage on error. It 176 | // defaults to calling f.PrintUsage(os.Stderr). 177 | func (s *Set) SetUsage(usage func()) { 178 | s.usage = usage 179 | } 180 | 181 | // Lookup returns the Option associated with name. Name should either be 182 | // a rune (the short name) or a string (the long name). 183 | func Lookup(name interface{}) Option { 184 | return CommandLine.Lookup(name) 185 | } 186 | 187 | // Lookup returns the Option associated with name in s. Name should either be 188 | // a rune (the short name) or a string (the long name). 189 | func (s *Set) Lookup(name interface{}) Option { 190 | switch v := name.(type) { 191 | case rune: 192 | return s.shortOptions[v] 193 | case int: 194 | return s.shortOptions[rune(v)] 195 | case string: 196 | return s.longOptions[v] 197 | } 198 | return nil 199 | } 200 | 201 | // IsSet returns true if the Option associated with name was seen while 202 | // parsing the command line arguments. Name should either be a rune (the 203 | // short name) or a string (the long name). 204 | func IsSet(name interface{}) bool { 205 | return CommandLine.IsSet(name) 206 | } 207 | 208 | // IsSet returns true if the Option associated with name was seen while 209 | // parsing s. Name should either be a rune (the short name) or a string (the 210 | // long name). 211 | func (s *Set) IsSet(name interface{}) bool { 212 | if opt := s.Lookup(name); opt != nil { 213 | return opt.Seen() 214 | } 215 | return false 216 | } 217 | 218 | // GetCount returns the number of times the Option associated with name has been 219 | // seen while parsing the command line arguments. Name should either be a rune 220 | // (the short name) or a string (the long name). 221 | func GetCount(name interface{}) int { 222 | return CommandLine.GetCount(name) 223 | } 224 | 225 | // GetCount returns the number of times the Option associated with name has been 226 | // seen while parsing s's arguments. Name should either be a rune (the short 227 | // name) or a string (the long name). 228 | func (s *Set) GetCount(name interface{}) int { 229 | if opt := s.Lookup(name); opt != nil { 230 | return opt.Count() 231 | } 232 | return 0 233 | } 234 | 235 | // GetValue returns the final value set to the command-line Option with name. 236 | // If the option has not been seen while parsing s then the default value is 237 | // returned. Name should either be a rune (the short name) or a string (the 238 | // long name). 239 | func GetValue(name interface{}) string { 240 | return CommandLine.GetValue(name) 241 | } 242 | 243 | // GetValue returns the final value set to the Option in s associated with name. 244 | // If the option has not been seen while parsing s then the default value is 245 | // returned. Name should either be a rune (the short name) or a string (the 246 | // long name). 247 | func (s *Set) GetValue(name interface{}) string { 248 | if opt := s.Lookup(name); opt != nil { 249 | return opt.String() 250 | } 251 | return "" 252 | } 253 | 254 | // Visit visits the command-line options in lexicographical order, calling fn 255 | // for each. It visits only those options that have been set. 256 | func Visit(fn func(Option)) { CommandLine.Visit(fn) } 257 | 258 | // Visit visits the options in s in lexicographical order, calling fn 259 | // for each. It visits only those options that have been set. 260 | func (s *Set) Visit(fn func(Option)) { 261 | sort.Sort(s.options) 262 | for _, opt := range s.options { 263 | if opt.count > 0 { 264 | fn(opt) 265 | } 266 | } 267 | } 268 | 269 | // VisitAll visits the options in s in lexicographical order, calling fn 270 | // for each. It visits all options, even those not set. 271 | func VisitAll(fn func(Option)) { CommandLine.VisitAll(fn) } 272 | 273 | // VisitAll visits the command-line flags in lexicographical order, calling fn 274 | // for each. It visits all flags, even those not set. 275 | func (s *Set) VisitAll(fn func(Option)) { 276 | sort.Sort(s.options) 277 | for _, opt := range s.options { 278 | fn(opt) 279 | } 280 | } 281 | 282 | // Reset resets all the command line options to the initial state so it 283 | // appears none of them have been seen. 284 | func Reset() { 285 | CommandLine.Reset() 286 | } 287 | 288 | // Reset resets all the options in s to the initial state so it 289 | // appears none of them have been seen. 290 | func (s *Set) Reset() { 291 | for _, opt := range s.options { 292 | opt.Reset() 293 | } 294 | } 295 | 296 | // RequiredGroup marks the group set with Option.SetGroup as required. At least 297 | // one option in the group must be seen by parse. Calling RequiredGroup with a 298 | // group name that has no options will cause parsing to always fail. 299 | func (s *Set) RequiredGroup(group string) { 300 | s.requiredGroups = append(s.requiredGroups, group) 301 | } 302 | 303 | // RequiredGroup marks the group set with Option.SetGroup as required on the 304 | // command line. At least one option in the group must be seen by parse. 305 | // Calling RequiredGroup with a group name that has no options will cause 306 | // parsing to always fail. 307 | func RequiredGroup(group string) { 308 | CommandLine.requiredGroups = append(CommandLine.requiredGroups, group) 309 | } 310 | -------------------------------------------------------------------------------- /v2/signed.go: -------------------------------------------------------------------------------- 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 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | "sync" 11 | ) 12 | 13 | type signed int64 14 | 15 | type SignedLimit struct { 16 | Base int // Base for conversion as per strconv.ParseInt 17 | Bits int // Number of bits as per strconv.ParseInt 18 | Min int64 // Minimum allowed value if both Min and Max are not 0 19 | Max int64 // Maximum allowed value if both Min and Max are not 0 20 | } 21 | 22 | var ( 23 | signedLimitsMu sync.Mutex 24 | signedLimits = make(map[*signed]*SignedLimit) 25 | ) 26 | 27 | func (n *signed) Set(value string, opt Option) error { 28 | signedLimitsMu.Lock() 29 | l := signedLimits[n] 30 | signedLimitsMu.Unlock() 31 | if l == nil { 32 | return fmt.Errorf("no limits defined for %s", opt.Name()) 33 | } 34 | v, err := strconv.ParseInt(value, l.Base, l.Bits) 35 | if err != nil { 36 | if e, ok := err.(*strconv.NumError); ok { 37 | switch e.Err { 38 | case strconv.ErrRange: 39 | err = fmt.Errorf("value out of range: %s", value) 40 | case strconv.ErrSyntax: 41 | err = fmt.Errorf("not a valid number: %s", value) 42 | } 43 | } 44 | return err 45 | } 46 | if l.Min != 0 || l.Max != 0 { 47 | if v < l.Min { 48 | return fmt.Errorf("value out of range (<%v): %s", l.Min, value) 49 | } 50 | if v > l.Max { 51 | return fmt.Errorf("value out of range (>%v): %s", l.Max, value) 52 | } 53 | } 54 | *n = signed(v) 55 | return nil 56 | } 57 | 58 | func (n *signed) String() string { 59 | signedLimitsMu.Lock() 60 | l := signedLimits[n] 61 | signedLimitsMu.Unlock() 62 | if l != nil && l.Base != 0 { 63 | return strconv.FormatInt(int64(*n), l.Base) 64 | } 65 | return strconv.FormatInt(int64(*n), 10) 66 | } 67 | 68 | // Signed creates an option that is stored in an int64 and is constrained 69 | // by the limits pointed to by l. The Max and Min values are only used if 70 | // at least one of the values are not 0. If Base is 0, the base is implied by 71 | // the string's prefix: base 16 for "0x", base 8 for "0", and base 10 otherwise. 72 | func Signed(name rune, value int64, l *SignedLimit, helpvalue ...string) *int64 { 73 | CommandLine.signedOption(&value, "", name, l, helpvalue...) 74 | return &value 75 | } 76 | 77 | func (s *Set) Signed(name rune, value int64, l *SignedLimit, helpvalue ...string) *int64 { 78 | s.signedOption(&value, "", name, l, helpvalue...) 79 | return &value 80 | } 81 | 82 | func SignedLong(name string, short rune, value int64, l *SignedLimit, helpvalue ...string) *int64 { 83 | CommandLine.signedOption(&value, name, short, l, helpvalue...) 84 | return &value 85 | } 86 | 87 | func (s *Set) SignedLong(name string, short rune, value int64, l *SignedLimit, helpvalue ...string) *int64 { 88 | s.signedOption(&value, name, short, l, helpvalue...) 89 | return &value 90 | } 91 | 92 | func (s *Set) signedOption(p *int64, name string, short rune, l *SignedLimit, helpvalue ...string) { 93 | opt := s.FlagLong((*signed)(p), name, short, helpvalue...) 94 | if l.Base > 36 || l.Base == 1 || l.Base < 0 { 95 | fmt.Fprintf(stderr, "invalid base for %s: %d\n", opt.Name(), l.Base) 96 | exit(1) 97 | } 98 | if l.Bits < 0 || l.Bits > 64 { 99 | fmt.Fprintf(stderr, "invalid bit size for %s: %d\n", opt.Name(), l.Bits) 100 | exit(1) 101 | } 102 | if l.Min > l.Max { 103 | fmt.Fprintf(stderr, "min greater than max for %s\n", opt.Name()) 104 | exit(1) 105 | } 106 | lim := *l 107 | signedLimitsMu.Lock() 108 | signedLimits[(*signed)(p)] = &lim 109 | signedLimitsMu.Unlock() 110 | } 111 | -------------------------------------------------------------------------------- /v2/signed_test.go: -------------------------------------------------------------------------------- 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 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var signedNumberTests = []struct { 14 | where string 15 | in []string 16 | l SignedLimit 17 | out int64 18 | err string 19 | }{ 20 | { 21 | where: loc(), 22 | }, 23 | { 24 | loc(), 25 | []string{"test", "-n", "1010"}, 26 | SignedLimit{Base: 2, Bits: 5}, 27 | 10, 28 | "", 29 | }, 30 | { 31 | loc(), 32 | []string{"test", "-n", "1010"}, 33 | SignedLimit{Base: 2, Bits: 4}, 34 | 0, 35 | "test: value out of range: 1010\n", 36 | }, 37 | { 38 | loc(), 39 | []string{"test", "-n", "-1000"}, 40 | SignedLimit{Base: 2, Bits: 4}, 41 | -8, 42 | "", 43 | }, 44 | { 45 | loc(), 46 | []string{"test", "-n", "3"}, 47 | SignedLimit{Min: 4, Max: 6}, 48 | 0, 49 | "test: value out of range (<4): 3\n", 50 | }, 51 | { 52 | loc(), 53 | []string{"test", "-n", "4"}, 54 | SignedLimit{Min: 4, Max: 6}, 55 | 4, 56 | "", 57 | }, 58 | { 59 | loc(), 60 | []string{"test", "-n", "5"}, 61 | SignedLimit{Min: 4, Max: 6}, 62 | 5, 63 | "", 64 | }, 65 | { 66 | loc(), 67 | []string{"test", "-n", "6"}, 68 | SignedLimit{Min: 4, Max: 6}, 69 | 6, 70 | "", 71 | }, 72 | { 73 | loc(), 74 | []string{"test", "-n", "7"}, 75 | SignedLimit{Min: 4, Max: 6}, 76 | 0, 77 | "test: value out of range (>6): 7\n", 78 | }, 79 | } 80 | 81 | func TestSigneds(t *testing.T) { 82 | for x, tt := range signedNumberTests { 83 | if strings.Index(tt.where, ":-") > 0 { 84 | tt.where = fmt.Sprintf("#%d", x) 85 | } 86 | 87 | reset() 88 | n := Signed('n', 0, &tt.l) 89 | parse(tt.in) 90 | if s := checkError(tt.err); s != "" { 91 | t.Errorf("%s: %s", tt.where, s) 92 | } 93 | if *n != tt.out { 94 | t.Errorf("%s: got %v, want %v", tt.where, *n, tt.out) 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /v2/string.go: -------------------------------------------------------------------------------- 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 | 5 | package getopt 6 | 7 | // String returns a value option that stores is value as a string. The 8 | // initial value of the string is passed in value. 9 | func String(name rune, value string, helpvalue ...string) *string { 10 | CommandLine.Flag(&value, name, helpvalue...) 11 | return &value 12 | } 13 | 14 | func (s *Set) String(name rune, value string, helpvalue ...string) *string { 15 | s.Flag(&value, name, helpvalue...) 16 | return &value 17 | } 18 | 19 | func StringLong(name string, short rune, value string, helpvalue ...string) *string { 20 | CommandLine.FlagLong(&value, name, short, helpvalue...) 21 | return &value 22 | } 23 | 24 | func (s *Set) StringLong(name string, short rune, value string, helpvalue ...string) *string { 25 | s.FlagLong(&value, name, short, helpvalue...) 26 | return &value 27 | } 28 | -------------------------------------------------------------------------------- /v2/string_test.go: -------------------------------------------------------------------------------- 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 | 5 | package getopt 6 | 7 | import "testing" 8 | 9 | var stringTests = []struct { 10 | where string 11 | in []string 12 | sout string 13 | optout string 14 | err string 15 | }{ 16 | { 17 | loc(), 18 | []string{}, 19 | "one", 20 | "two", 21 | "", 22 | }, 23 | { 24 | loc(), 25 | []string{"test", "-s"}, 26 | "one", 27 | "two", 28 | "test: missing parameter for -s\n", 29 | }, 30 | { 31 | loc(), 32 | []string{"test", "--opt"}, 33 | "one", 34 | "two", 35 | "test: missing parameter for --opt\n", 36 | }, 37 | { 38 | loc(), 39 | []string{"test", "-svalue", "--opt=option"}, 40 | "value", 41 | "option", 42 | "", 43 | }, 44 | { 45 | loc(), 46 | []string{"test", "-s", "value", "--opt", "option"}, 47 | "value", 48 | "option", 49 | "", 50 | }, 51 | { 52 | loc(), 53 | []string{"test", "-swrong", "--opt=wrong", "-s", "value", "--opt", "option"}, 54 | "value", 55 | "option", 56 | "", 57 | }, 58 | } 59 | 60 | func TestString(t *testing.T) { 61 | for _, tt := range stringTests { 62 | reset() 63 | s := String('s', "one") 64 | opt := StringLong("opt", 0, "two") 65 | 66 | parse(tt.in) 67 | if s := checkError(tt.err); s != "" { 68 | t.Errorf("%s: %s", tt.where, s) 69 | } 70 | if *s != tt.sout { 71 | t.Errorf("%s: got s = %q, want %q", tt.where, *s, tt.sout) 72 | } 73 | if *opt != tt.optout { 74 | t.Errorf("%s: got opt = %q, want %q", tt.where, *opt, tt.optout) 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /v2/unsigned.go: -------------------------------------------------------------------------------- 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 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | "sync" 11 | ) 12 | 13 | type unsigned uint64 14 | 15 | type UnsignedLimit struct { 16 | Base int // Base for conversion as per strconv.ParseInt 17 | Bits int // Number of bits as per strconv.ParseInt 18 | Min uint64 // Minimum allowed value if both Min and Max are not 0 19 | Max uint64 // Maximum allowed value if both Min and Max are not 0 20 | } 21 | 22 | var ( 23 | unsignedLimitsMu sync.Mutex 24 | unsignedLimits = make(map[*unsigned]*UnsignedLimit) 25 | ) 26 | 27 | func (n *unsigned) Set(value string, opt Option) error { 28 | unsignedLimitsMu.Lock() 29 | l := unsignedLimits[n] 30 | unsignedLimitsMu.Unlock() 31 | if l == nil { 32 | return fmt.Errorf("no limits defined for %s", opt.Name()) 33 | } 34 | v, err := strconv.ParseUint(value, l.Base, l.Bits) 35 | if err != nil { 36 | if e, ok := err.(*strconv.NumError); ok { 37 | switch e.Err { 38 | case strconv.ErrRange: 39 | err = fmt.Errorf("value out of range: %s", value) 40 | case strconv.ErrSyntax: 41 | err = fmt.Errorf("not a valid number: %s", value) 42 | } 43 | } 44 | return err 45 | } 46 | if l.Min != 0 || l.Max != 0 { 47 | if v < l.Min { 48 | return fmt.Errorf("value out of range (<%v): %s", l.Min, value) 49 | } 50 | if v > l.Max { 51 | return fmt.Errorf("value out of range (>%v): %s", l.Max, value) 52 | } 53 | } 54 | *n = unsigned(v) 55 | return nil 56 | } 57 | 58 | func (n *unsigned) String() string { 59 | unsignedLimitsMu.Lock() 60 | l := unsignedLimits[n] 61 | unsignedLimitsMu.Unlock() 62 | if l != nil && l.Base != 0 { 63 | return strconv.FormatUint(uint64(*n), l.Base) 64 | } 65 | return strconv.FormatUint(uint64(*n), 10) 66 | } 67 | 68 | // Unsigned creates an option that is stored in a uint64 and is 69 | // constrained by the limits pointed to by l. The Max and Min values are only 70 | // used if at least one of the values are not 0. If Base is 0, the base is 71 | // implied by the string's prefix: base 16 for "0x", base 8 for "0", and base 72 | // 10 otherwise. 73 | func Unsigned(name rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 { 74 | CommandLine.unsignedOption(&value, "", name, l, helpvalue...) 75 | return &value 76 | } 77 | 78 | func (s *Set) Unsigned(name rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 { 79 | s.unsignedOption(&value, "", name, l, helpvalue...) 80 | return &value 81 | } 82 | 83 | func UnsignedLong(name string, short rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 { 84 | CommandLine.unsignedOption(&value, name, short, l, helpvalue...) 85 | return &value 86 | } 87 | 88 | func (s *Set) UnsignedLong(name string, short rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 { 89 | s.unsignedOption(&value, name, short, l, helpvalue...) 90 | return &value 91 | } 92 | 93 | func (s *Set) unsignedOption(p *uint64, name string, short rune, l *UnsignedLimit, helpvalue ...string) { 94 | opt := s.FlagLong((*unsigned)(p), name, short, helpvalue...) 95 | if l.Base > 36 || l.Base == 1 || l.Base < 0 { 96 | fmt.Fprintf(stderr, "invalid base for %s: %d\n", opt.Name(), l.Base) 97 | exit(1) 98 | } 99 | if l.Bits < 0 || l.Bits > 64 { 100 | fmt.Fprintf(stderr, "invalid bit size for %s: %d\n", opt.Name(), l.Bits) 101 | exit(1) 102 | } 103 | if l.Min > l.Max { 104 | fmt.Fprintf(stderr, "min greater than max for %s\n", opt.Name()) 105 | exit(1) 106 | } 107 | lim := *l 108 | unsignedLimitsMu.Lock() 109 | unsignedLimits[(*unsigned)(p)] = &lim 110 | unsignedLimitsMu.Unlock() 111 | } 112 | -------------------------------------------------------------------------------- /v2/unsigned_test.go: -------------------------------------------------------------------------------- 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 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | var unsignedTests = []struct { 14 | where string 15 | in []string 16 | l UnsignedLimit 17 | out uint64 18 | err string 19 | }{ 20 | { 21 | where: loc(), 22 | }, 23 | { 24 | loc(), 25 | []string{"test", "-n", "1010"}, 26 | UnsignedLimit{Base: 2, Bits: 5}, 27 | 10, 28 | "", 29 | }, 30 | { 31 | loc(), 32 | []string{"test", "-n", "1010"}, 33 | UnsignedLimit{Base: 2, Bits: 4}, 34 | 10, 35 | "", 36 | }, 37 | { 38 | loc(), 39 | []string{"test", "-n", "1010"}, 40 | UnsignedLimit{Base: 2, Bits: 3}, 41 | 0, 42 | "test: value out of range: 1010\n", 43 | }, 44 | { 45 | loc(), 46 | []string{"test", "-n", "3"}, 47 | UnsignedLimit{Min: 4, Max: 6}, 48 | 0, 49 | "test: value out of range (<4): 3\n", 50 | }, 51 | { 52 | loc(), 53 | []string{"test", "-n", "4"}, 54 | UnsignedLimit{Min: 4, Max: 6}, 55 | 4, 56 | "", 57 | }, 58 | { 59 | loc(), 60 | []string{"test", "-n", "5"}, 61 | UnsignedLimit{Min: 4, Max: 6}, 62 | 5, 63 | "", 64 | }, 65 | { 66 | loc(), 67 | []string{"test", "-n", "6"}, 68 | UnsignedLimit{Min: 4, Max: 6}, 69 | 6, 70 | "", 71 | }, 72 | { 73 | loc(), 74 | []string{"test", "-n", "7"}, 75 | UnsignedLimit{Min: 4, Max: 6}, 76 | 0, 77 | "test: value out of range (>6): 7\n", 78 | }, 79 | } 80 | 81 | func TestUnsigneds(t *testing.T) { 82 | for x, tt := range unsignedTests { 83 | if strings.Index(tt.where, ":-") > 0 { 84 | tt.where = fmt.Sprintf("#%d", x) 85 | } 86 | 87 | reset() 88 | n := Unsigned('n', 0, &tt.l) 89 | parse(tt.in) 90 | if s := checkError(tt.err); s != "" { 91 | t.Errorf("%s: %s", tt.where, s) 92 | } 93 | if *n != tt.out { 94 | t.Errorf("%s: got %v, want %v", tt.where, *n, tt.out) 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /v2/util_test.go: -------------------------------------------------------------------------------- 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 | 5 | package getopt 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "path" 11 | "reflect" 12 | "runtime" 13 | "strings" 14 | ) 15 | 16 | var errorString string 17 | 18 | func reset() { 19 | CommandLine.shortOptions = make(map[rune]*option) 20 | CommandLine.longOptions = make(map[string]*option) 21 | CommandLine.options = nil 22 | CommandLine.args = nil 23 | CommandLine.program = "" 24 | CommandLine.requiredGroups = nil 25 | errorString = "" 26 | } 27 | 28 | func parse(args []string) { 29 | err := CommandLine.Getopt(args, nil) 30 | if err != nil { 31 | b := &bytes.Buffer{} 32 | 33 | fmt.Fprintln(b, CommandLine.program+": "+err.Error()) 34 | CommandLine.PrintUsage(b) 35 | errorString = b.String() 36 | } 37 | } 38 | 39 | func badSlice(a, b []string) bool { 40 | if len(a) != len(b) { 41 | return true 42 | } 43 | for x, v := range a { 44 | if b[x] != v { 45 | return true 46 | } 47 | } 48 | return false 49 | } 50 | 51 | func loc() string { 52 | _, file, line, _ := runtime.Caller(1) 53 | return fmt.Sprintf("%s:%d", path.Base(file), line) 54 | } 55 | 56 | func (o *option) Equal(opt *option) bool { 57 | if o.value != nil && opt.value == nil { 58 | return false 59 | } 60 | if o.value == nil && opt.value != nil { 61 | return false 62 | } 63 | if o.value != nil && o.value.String() != opt.value.String() { 64 | return false 65 | } 66 | 67 | oc := *o 68 | optc := *opt 69 | oc.value = nil 70 | optc.value = nil 71 | return reflect.DeepEqual(&oc, &optc) 72 | } 73 | 74 | func checkError(err string) string { 75 | switch { 76 | case err == errorString: 77 | return "" 78 | case err == "": 79 | return fmt.Sprintf("unexpected error %q", errorString) 80 | case errorString == "": 81 | return fmt.Sprintf("did not get expected error %q", err) 82 | case !strings.HasPrefix(errorString, err): 83 | return fmt.Sprintf("got error %q, want %q", errorString, err) 84 | } 85 | return "" 86 | } 87 | -------------------------------------------------------------------------------- /v2/var.go: -------------------------------------------------------------------------------- 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 | 5 | package getopt 6 | 7 | import ( 8 | "fmt" 9 | "runtime" 10 | "strings" 11 | ) 12 | 13 | // Value is the interface to the dynamic value stored in a flag. Flags of type 14 | // Value are declared using the Flag and FlagLong functions. 15 | type Value interface { 16 | // Set converts value into the appropriate type and assigns it to the 17 | // receiver value. Option details are provided via opt (such as the 18 | // flags name). 19 | // 20 | // Set is used to reset the value of an option to its default value 21 | // (which is stored in string form internally). 22 | Set(value string, opt Option) error 23 | 24 | // String returns the value of the flag as a string. 25 | String() string 26 | } 27 | 28 | var thisPackage string 29 | 30 | // init initializes thisPackage to our full package with the trailing . 31 | // included. 32 | func init() { 33 | pc, _, _, ok := runtime.Caller(0) 34 | if !ok { 35 | return 36 | } 37 | f := runtime.FuncForPC(pc) 38 | if f == nil { 39 | return 40 | } 41 | thisPackage = f.Name() 42 | x := strings.LastIndex(thisPackage, "/") 43 | if x < 0 { 44 | return 45 | } 46 | y := strings.Index(thisPackage[x:], ".") 47 | if y < 0 { 48 | return 49 | } 50 | // thisPackage includes the trailing . after the package name. 51 | thisPackage = thisPackage[:x+y+1] 52 | } 53 | 54 | // calledFrom returns a string containing the file and linenumber of the first 55 | // stack frame above us that is not part of this package and is not a test. 56 | // This is used to determine where a flag was initialized. 57 | func calledFrom() string { 58 | for i := 2; ; i++ { 59 | pc, file, line, ok := runtime.Caller(i) 60 | if !ok { 61 | return "" 62 | } 63 | if !strings.HasSuffix(file, "_test.go") { 64 | f := runtime.FuncForPC(pc) 65 | if f != nil && strings.HasPrefix(f.Name(), thisPackage) { 66 | continue 67 | } 68 | } 69 | return fmt.Sprintf("%s:%d", file, line) 70 | } 71 | } 72 | 73 | func (s *Set) addFlag(p Value, name string, short rune, helpvalue ...string) Option { 74 | opt := &option{ 75 | short: short, 76 | long: name, 77 | value: p, 78 | defval: p.String(), 79 | } 80 | 81 | switch len(helpvalue) { 82 | case 2: 83 | opt.name = helpvalue[1] 84 | fallthrough 85 | case 1: 86 | opt.help = helpvalue[0] 87 | case 0: 88 | default: 89 | panic("Too many strings for String helpvalue") 90 | } 91 | if where := calledFrom(); where != "" { 92 | opt.where = where 93 | } 94 | if opt.short == 0 && opt.long == "" { 95 | fmt.Fprintf(stderr, opt.where+": no short or long option given") 96 | exit(1) 97 | } 98 | s.AddOption(opt) 99 | return opt 100 | } 101 | -------------------------------------------------------------------------------- /var.go: -------------------------------------------------------------------------------- 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 6 | 7 | import ( 8 | "fmt" 9 | "runtime" 10 | ) 11 | 12 | // Value is the interface to the dynamic value stored in a flag. (The default 13 | // value is represented as a string.) Set is passed the string to set the 14 | // value to as well as the Option that is being processed. 15 | type Value interface { 16 | Set(string, Option) error 17 | String() string 18 | } 19 | 20 | // Var creates an option of the specified name. The type and value of the option 21 | // are represented by the first argument, of type Value, which typically holds a 22 | // user-defined implementation of Value. All options are ultimately created 23 | // as a Var. 24 | func Var(p Value, name rune, helpvalue ...string) Option { 25 | return CommandLine.VarLong(p, "", name, helpvalue...) 26 | } 27 | 28 | func VarLong(p Value, name string, short rune, helpvalue ...string) Option { 29 | return CommandLine.VarLong(p, name, short, helpvalue...) 30 | } 31 | 32 | func (s *Set) Var(p Value, name rune, helpvalue ...string) Option { 33 | return s.VarLong(p, "", name, helpvalue...) 34 | } 35 | 36 | func (s *Set) VarLong(p Value, name string, short rune, helpvalue ...string) Option { 37 | opt := &option{ 38 | short: short, 39 | long: name, 40 | value: p, 41 | defval: p.String(), 42 | } 43 | 44 | switch len(helpvalue) { 45 | case 2: 46 | opt.name = helpvalue[1] 47 | fallthrough 48 | case 1: 49 | opt.help = helpvalue[0] 50 | case 0: 51 | default: 52 | panic("Too many strings for String helpvalue") 53 | } 54 | if _, file, line, ok := runtime.Caller(1); ok { 55 | opt.where = fmt.Sprintf("%s:%d", file, line) 56 | } 57 | if opt.short == 0 && opt.long == "" { 58 | fmt.Fprintf(stderr, opt.where+": no short or long option given") 59 | exit(1) 60 | } 61 | s.AddOption(opt) 62 | return opt 63 | } 64 | --------------------------------------------------------------------------------