├── LICENSE ├── README.md ├── getopt_test.go └── getopt.go /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. 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 Inc. nor the names of its 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 | # rsc.io/getopt 2 | 3 | [For full package documentation, see [https://godoc.org/rsc.io/getopt](https://godoc.org/rsc.io/getopt).] 4 | 5 | package getopt // import "rsc.io/getopt" 6 | 7 | Package getopt parses command lines using [_getopt_(3)](http://man7.org/linux/man-pages/man3/getopt.3.html) syntax. It is a 8 | replacement for `flag.Parse` but still expects flags themselves to be defined 9 | in package flag. 10 | 11 | Flags defined with one-letter names are available as short flags (invoked 12 | using one dash, as in `-x`) and all flags are available as long flags (invoked 13 | using two dashes, as in `--x` or `--xylophone`). 14 | 15 | To use, define flags as usual with [package flag](https://godoc.org/flag). Then introduce any aliases 16 | by calling `getopt.Alias`: 17 | 18 | getopt.Alias("v", "verbose") 19 | 20 | Or call `getopt.Aliases` to define a list of aliases: 21 | 22 | getopt.Aliases( 23 | "v", "verbose", 24 | "x", "xylophone", 25 | ) 26 | 27 | One name in each pair must already be defined in package flag (so either 28 | "v" or "verbose", and also either "x" or "xylophone"). 29 | 30 | Then parse the command-line: 31 | 32 | getopt.Parse() 33 | 34 | If it encounters an error, `Parse` calls `flag.Usage` and then exits the 35 | program. 36 | 37 | When writing a custom `flag.Usage` function, call `getopt.PrintDefaults` instead 38 | of `flag.PrintDefaults` to get a usage message that includes the 39 | names of aliases in flag descriptions. 40 | 41 | At initialization time, package getopt installs a new `flag.Usage` that is the same 42 | as the default `flag.Usage` except that it calls `getopt.PrintDefaults` instead 43 | of `flag.PrintDefaults`. 44 | 45 | This package also defines a `FlagSet` wrapping the standard `flag.FlagSet`. 46 | 47 | ## Caveat 48 | 49 | In general Go flag parsing is preferred for new programs, because it is not 50 | as pedantic about the number of dashes used to invoke a flag (you can write 51 | `-verbose` or `--verbose` and the program does not care). This package is meant 52 | to be used in situations where, for legacy reasons, it is important to use 53 | exactly _getopt_(3) syntax, such as when rewriting in Go an existing tool that 54 | already uses _getopt_(3). 55 | -------------------------------------------------------------------------------- /getopt_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. 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 | "flag" 10 | "fmt" 11 | "strings" 12 | "testing" 13 | ) 14 | 15 | // TODO: Real tests. 16 | 17 | type testFlagSet struct { 18 | A *bool 19 | B *bool 20 | C *bool 21 | D *bool 22 | I *int 23 | Long *int 24 | S *string 25 | Args []string 26 | 27 | t *testing.T 28 | buf bytes.Buffer 29 | flag *FlagSet 30 | } 31 | 32 | func (tf *testFlagSet) str() string { 33 | out := "" 34 | if *tf.A { 35 | out += " -a" 36 | } 37 | if *tf.B { 38 | out += " -b" 39 | } 40 | if *tf.C { 41 | out += " -c" 42 | } 43 | if *tf.D { 44 | out += " -d" 45 | } 46 | if *tf.I != 0 { 47 | out += fmt.Sprintf(" -i %d", *tf.I) 48 | } 49 | if *tf.Long != 0 { 50 | out += fmt.Sprintf(" --long %d", *tf.Long) 51 | } 52 | if *tf.S != "" { 53 | out += " -s " + *tf.S 54 | } 55 | if len(tf.Args) > 0 { 56 | out += " " + strings.Join(tf.Args, " ") 57 | } 58 | if out == "" { 59 | return out 60 | } 61 | return out[1:] 62 | } 63 | 64 | func newTestFlagSet(t *testing.T) *testFlagSet { 65 | tf := &testFlagSet{t: t, flag: NewFlagSet("x", flag.ContinueOnError)} 66 | f := tf.flag 67 | f.SetOutput(&tf.buf) 68 | tf.A = f.Bool("a", false, "desc of a") 69 | tf.B = f.Bool("b", false, "desc of b") 70 | tf.C = f.Bool("c", false, "desc of c") 71 | tf.D = f.Bool("d", false, "desc of d") 72 | tf.Long = f.Int("long", 0, "long only") 73 | f.Alias("a", "aah") 74 | f.Aliases("b", "beeta", "c", "charlie") 75 | tf.I = f.Int("i", 0, "i") 76 | f.Alias("i", "india") 77 | tf.S = f.String("sierra", "", "string") 78 | f.Alias("s", "sierra") 79 | 80 | return tf 81 | } 82 | 83 | var tests = []struct { 84 | cmd string 85 | out string 86 | }{ 87 | {"-i 1", "-i 1"}, 88 | {"--india 1", "-i 1"}, 89 | {"--india=1", "-i 1"}, 90 | {"-i=1", `ERR: invalid value "=1" for flag -i: strconv.ParseInt: parsing "=1": invalid syntax`}, 91 | {"--i=1", "-i 1"}, 92 | {"-abc", "-a -b -c"}, 93 | {"--abc", `ERR: flag provided but not defined: --abc`}, 94 | {"-sfoo", "-s foo"}, 95 | {"-s foo", "-s foo"}, 96 | {"--s=foo", "-s foo"}, 97 | {"-s=foo", "-s =foo"}, 98 | {"-s", `ERR: missing argument for -s`}, 99 | {"--s", `ERR: missing argument for --s`}, 100 | {"--s=", ``}, 101 | {"-sfooi1 -i2", "-i 2 -s fooi1"}, 102 | {"-absfoo", "-a -b -s foo"}, 103 | {"-i1 -- arg", "-i 1 arg"}, 104 | {"-i1 - arg", "-i 1 - arg"}, 105 | {"-i1 --- arg", `ERR: flag provided but not defined: ---`}, 106 | {"-i1 arg", "-i 1 arg"}, 107 | {"--aah --charlie --beeta --sierra=123", "-a -b -c -s 123"}, 108 | {"-i1 --long=2", "-i 1 --long 2"}, 109 | } 110 | 111 | func TestBasic(t *testing.T) { 112 | for _, tt := range tests { 113 | tf := newTestFlagSet(t) 114 | err := tf.flag.Parse(strings.Fields(tt.cmd)) 115 | var out string 116 | if err != nil { 117 | out = "ERR: " + err.Error() 118 | } else { 119 | tf.Args = tf.flag.Args() 120 | out = tf.str() 121 | } 122 | if out != tt.out { 123 | t.Errorf("%s:\nhave %s\nwant %s", tt.cmd, out, tt.out) 124 | } 125 | } 126 | } 127 | 128 | var wantHelpText = ` -a, --aah 129 | desc of a 130 | -b, --beeta 131 | desc of b 132 | -c, --charlie 133 | desc of c 134 | -d desc of d 135 | -i, --india int 136 | i 137 | --long int 138 | long only 139 | -s, --sierra string 140 | string 141 | ` 142 | 143 | func TestHelpText(t *testing.T) { 144 | tf := newTestFlagSet(t) 145 | tf.flag.PrintDefaults() 146 | out := tf.buf.String() 147 | if out != wantHelpText { 148 | t.Errorf("have<\n%s>\nwant<\n%s>", out, wantHelpText) 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /getopt.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. 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 parses command lines using getopt(3) syntax. 6 | // It is a replacement for flag.Parse but still expects flags themselves 7 | // to be defined in package flag. 8 | // 9 | // Flags defined with one-letter names are available as short flags 10 | // (invoked using one dash, as in -x) and all flags are available as 11 | // long flags (invoked using two dashes, as in --x or --xylophone). 12 | // 13 | // To use, define flags as usual with package flag. Then introduce 14 | // any aliases by calling getopt.Alias: 15 | // 16 | // getopt.Alias("n", "dry-run") 17 | // getopt.Alias("v", "verbose") 18 | // 19 | // Or call getopt.Aliases to define a list of aliases: 20 | // 21 | // getopt.Aliases( 22 | // "n", "dry-run", 23 | // "v", "verbose", 24 | // ) 25 | // 26 | // One name in each pair must already be defined in package flag 27 | // (so either "n" or "dry-run", and also either "v" or "verbose"). 28 | // 29 | // Then parse the command-line: 30 | // 31 | // getopt.Parse() 32 | // 33 | // If it encounters an error, Parse calls flag.Usage and then exits the program. 34 | // 35 | // When writing a custom flag.Usage function, call getopt.PrintDefaults 36 | // instead of flag.PrintDefaults to get a usage message that includes the 37 | // names of aliases in flag descriptions. 38 | // 39 | // At initialization time, this package installs a new flag.Usage that is the 40 | // same as the default flag.Usage except that it calls getopt.PrintDefaults 41 | // instead of flag.PrintDefaults. 42 | // 43 | // This package also defines a FlagSet wrapping the standard flag.FlagSet. 44 | // 45 | // Caveat 46 | // 47 | // In general Go flag parsing is preferred for new programs, because 48 | // it is not as pedantic about the number of dashes used to invoke 49 | // a flag (you can write -verbose or --verbose and the program 50 | // does not care). This package is meant to be used in situations 51 | // where, for legacy reasons, it is important to use exactly getopt(3) 52 | // syntax, such as when rewriting in Go an existing tool that already 53 | // uses getopt(3). 54 | package getopt // import "rsc.io/getopt" 55 | 56 | import ( 57 | "flag" 58 | "fmt" 59 | "io" 60 | "os" 61 | "reflect" 62 | "strings" 63 | "unicode/utf8" 64 | ) 65 | 66 | func init() { 67 | flag.Usage = func() { 68 | fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) 69 | PrintDefaults() // ours not package flag's 70 | } 71 | 72 | CommandLine.FlagSet = flag.CommandLine 73 | CommandLine.name = os.Args[0] 74 | CommandLine.errorHandling = flag.ExitOnError 75 | CommandLine.outw = os.Stderr 76 | CommandLine.Usage = func() { flag.Usage() } 77 | } 78 | 79 | var CommandLine FlagSet 80 | 81 | // A FlagSet is a set of defined flags. 82 | // It wraps and provides the same interface as flag.FlagSet 83 | // but parses command line arguments using getopt syntax. 84 | // 85 | // Note that "go doc" shows only the methods customized 86 | // by package getopt; FlagSet also provides all the methods 87 | // of the embedded flag.FlagSet, like Bool, Int, NArg, and so on. 88 | type FlagSet struct { 89 | *flag.FlagSet 90 | 91 | alias map[string]string 92 | unalias map[string]string 93 | name string 94 | errorHandling flag.ErrorHandling 95 | outw io.Writer 96 | } 97 | 98 | func (f *FlagSet) out() io.Writer { 99 | if f.outw == nil { 100 | return os.Stderr 101 | } 102 | return f.outw 103 | } 104 | 105 | // SetOutput sets the destination for usage and error messages. 106 | // If output is nil, os.Stderr is used. 107 | func (f *FlagSet) SetOutput(output io.Writer) { 108 | f.FlagSet.SetOutput(output) 109 | f.outw = output 110 | } 111 | 112 | // NewFlagSet returns a new, empty flag set with the specified name and error 113 | // handling property. 114 | func NewFlagSet(name string, errorHandling flag.ErrorHandling) *FlagSet { 115 | f := new(FlagSet) 116 | f.Init(name, errorHandling) 117 | return f 118 | } 119 | 120 | // Init sets the name and error handling proprety for a flag set. 121 | func (f *FlagSet) Init(name string, errorHandling flag.ErrorHandling) { 122 | if f.FlagSet == nil { 123 | f.FlagSet = new(flag.FlagSet) 124 | } 125 | f.FlagSet.Init(name, errorHandling) 126 | f.name = name 127 | f.errorHandling = errorHandling 128 | f.FlagSet.Usage = f.defaultUsage 129 | } 130 | 131 | func (f *FlagSet) init() { 132 | if f.alias == nil { 133 | f.alias = make(map[string]string) 134 | f.unalias = make(map[string]string) 135 | } 136 | } 137 | 138 | // Lookup returns the Flag structure of the named flag, 139 | // returning nil if none exists. 140 | // If name is a defined alias for a defined flag, 141 | // Lookup returns the original flag; in this case 142 | // the Name field in the result will differ from the 143 | // name passed to Lookup. 144 | func (f *FlagSet) Lookup(name string) *flag.Flag { 145 | if x, ok := f.alias[name]; ok { 146 | name = x 147 | } 148 | return f.FlagSet.Lookup(name) 149 | } 150 | 151 | // Alias introduces an alias for an existing flag name. 152 | // The short name must be a single letter, and the long name must be multiple letters. 153 | // Exactly one name must be defined as a flag already: the undefined name is introduced 154 | // as an alias for the defined name. 155 | // Alias panics if both names are already defined or if both are undefined. 156 | // 157 | // For example, if a flag named "v" is already defined using package flag, 158 | // then it is available as -v (or --v). Calling Alias("v", "verbose") makes the same 159 | // flag also available as --verbose. 160 | func Alias(short, long string) { 161 | CommandLine.Alias(short, long) 162 | } 163 | 164 | // Alias introduces an alias for an existing flag name. 165 | // The short name must be a single letter, and the long name must be multiple letters. 166 | // Exactly one name must be defined as a flag already: the undefined name is introduced 167 | // as an alias for the defined name. 168 | // Alias panics if both names are already defined or if both are undefined. 169 | // 170 | // For example, if a flag named "v" is already defined using package flag, 171 | // then it is available as -v (or --v). Calling Alias("v", "verbose") makes the same 172 | // flag also available as --verbose. 173 | func (f *FlagSet) Alias(short, long string) { 174 | f.init() 175 | if short == "" || long == "" { 176 | panic("Alias: invalid empty flag name") 177 | } 178 | if utf8.RuneCountInString(short) != 1 { 179 | panic("Alias: invalid short flag name -" + short) 180 | } 181 | if utf8.RuneCountInString(long) == 1 { 182 | panic("Alias: invalid long flag name --" + long) 183 | } 184 | 185 | f1 := f.Lookup(short) 186 | f2 := f.Lookup(long) 187 | if f1 == nil && f2 == nil { 188 | panic("Alias: neither -" + short + " nor -" + long + " is a defined flag") 189 | } 190 | if f1 != nil && f2 != nil { 191 | panic("Alias: both -" + short + " and -" + long + " are defined flags") 192 | } 193 | 194 | if f1 != nil { 195 | f.alias[long] = short 196 | f.unalias[short] = long 197 | } else { 198 | f.alias[short] = long 199 | f.unalias[long] = short 200 | } 201 | } 202 | 203 | // Aliases introduces zero or more aliases. The argument list must consist of an 204 | // even number of strings making up a sequence of short, long pairs to be passed 205 | // to Alias. 206 | func Aliases(list ...string) { 207 | CommandLine.Aliases(list...) 208 | } 209 | 210 | // Aliases introduces zero or more aliases. The argument list must consist of an 211 | // even number of strings making up a sequence of short, long pairs to be passed 212 | // to Alias. 213 | func (f *FlagSet) Aliases(list ...string) { 214 | if len(list)%2 != 0 { 215 | panic("getopt: Aliases not invoked with pairs") 216 | } 217 | for i := 0; i < len(list); i += 2 { 218 | f.Alias(list[i], list[i+1]) 219 | } 220 | } 221 | 222 | type boolFlag interface { 223 | IsBoolFlag() bool 224 | } 225 | 226 | func (f *FlagSet) failf(format string, args ...interface{}) error { 227 | err := fmt.Errorf(format, args...) 228 | fmt.Fprintln(f.out(), err) 229 | f.Usage() 230 | return err 231 | } 232 | 233 | // defaultUsage is the default function to print a usage message. 234 | func (f *FlagSet) defaultUsage() { 235 | if f.name == "" { 236 | fmt.Fprintf(f.out(), "Usage:\n") 237 | } else { 238 | fmt.Fprintf(f.out(), "Usage of %s:\n", f.name) 239 | } 240 | f.PrintDefaults() 241 | } 242 | 243 | // Parse parses the command-line flags from os.Args[1:]. 244 | func Parse() { 245 | CommandLine.Parse(os.Args[1:]) 246 | } 247 | 248 | // Parse parses flag definitions from the argument list, 249 | // which should not include the command name. 250 | // Parse must be called after all flags and aliases in the FlagSet are defined 251 | // and before flags are accessed by the program. 252 | // The return value will be flag.ErrHelp if -h or --help were used but not defined. 253 | func (f *FlagSet) Parse(args []string) error { 254 | for len(args) > 0 { 255 | arg := args[0] 256 | if len(arg) < 2 || arg[0] != '-' { 257 | break 258 | } 259 | args = args[1:] 260 | if arg[:2] == "--" { 261 | // Process single long option. 262 | if arg == "--" { 263 | break 264 | } 265 | name := arg[2:] 266 | value := "" 267 | haveValue := false 268 | if i := strings.Index(name, "="); i >= 0 { 269 | name, value = name[:i], name[i+1:] 270 | haveValue = true 271 | } 272 | fg := f.Lookup(name) 273 | if fg == nil { 274 | if name == "h" || name == "help" { 275 | // TODO ErrHelp 276 | } 277 | return f.failf("flag provided but not defined: --%s", name) 278 | } 279 | if b, ok := fg.Value.(boolFlag); ok && b.IsBoolFlag() { 280 | if haveValue { 281 | if err := fg.Value.Set(value); err != nil { 282 | return f.failf("invalid boolean value %q for --%s: %v", value, name, err) 283 | } 284 | } else { 285 | if err := fg.Value.Set("true"); err != nil { 286 | return f.failf("invalid boolean flag %s: %v", name, err) 287 | } 288 | } 289 | continue 290 | } 291 | if !haveValue { 292 | if len(args) == 0 { 293 | return f.failf("missing argument for --%s", name) 294 | } 295 | value, args = args[0], args[1:] 296 | } 297 | if err := fg.Value.Set(value); err != nil { 298 | return f.failf("invalid value %q for flag --%s: %v", value, name, err) 299 | } 300 | continue 301 | } 302 | 303 | // Process one or more short options. 304 | for arg = arg[1:]; arg != ""; { 305 | r, size := utf8.DecodeRuneInString(arg) 306 | if r == utf8.RuneError && size == 1 { 307 | return f.failf("invalid UTF8 in command-line flags") 308 | } 309 | name := arg[:size] 310 | arg = arg[size:] 311 | fg := f.Lookup(name) 312 | if fg == nil { 313 | if name == "h" { 314 | // TODO ErrHelp 315 | } 316 | return f.failf("flag provided but not defined: -%s", name) 317 | } 318 | if b, ok := fg.Value.(boolFlag); ok && b.IsBoolFlag() { 319 | if err := fg.Value.Set("true"); err != nil { 320 | return f.failf("invalid boolean flag %s: %v", name, err) 321 | } 322 | continue 323 | } 324 | if arg == "" { 325 | if len(args) == 0 { 326 | return f.failf("missing argument for -%s", name) 327 | } 328 | arg, args = args[0], args[1:] 329 | } 330 | if err := fg.Value.Set(arg); err != nil { 331 | return f.failf("invalid value %q for flag -%s: %v", arg, name, err) 332 | } 333 | break // consumed arg 334 | } 335 | } 336 | 337 | // Arrange for flag.NArg, flag.Args, etc to work properly. 338 | f.FlagSet.Parse(append([]string{"--"}, args...)) 339 | return nil 340 | } 341 | 342 | // PrintDefaults is like flag.PrintDefaults but includes information 343 | // about short/long alias pairs and prints the correct syntax for 344 | // long flags. 345 | func PrintDefaults() { 346 | CommandLine.PrintDefaults() 347 | } 348 | 349 | // PrintDefaults is like flag.PrintDefaults but includes information 350 | // about short/long alias pairs and prints the correct syntax for 351 | // long flags. 352 | func (f *FlagSet) PrintDefaults() { 353 | f.FlagSet.VisitAll(func(fg *flag.Flag) { 354 | name := fg.Name 355 | short, long := "", "" 356 | other := f.unalias[name] 357 | if utf8.RuneCountInString(name) > 1 { 358 | long, short = name, other 359 | } else { 360 | short, long = name, other 361 | } 362 | var s string 363 | if short != "" { 364 | s = fmt.Sprintf(" -%s", short) // Two spaces before -; see next two comments. 365 | if long != "" { 366 | s += ", --" + long 367 | } 368 | } else { 369 | s = fmt.Sprintf(" --%s", long) // Two spaces before -; see next two comments. 370 | } 371 | name, usage := flag.UnquoteUsage(fg) 372 | if len(name) > 0 { 373 | s += " " + name 374 | } 375 | 376 | // Boolean flags of one ASCII letter are so common we 377 | // treat them specially, putting their usage on the same line. 378 | if len(s) <= 4 { // space, space, '-', 'x'. 379 | s += "\t" 380 | } else { 381 | // Four spaces before the tab triggers good alignment 382 | // for both 4- and 8-space tab stops. 383 | s += "\n \t" 384 | } 385 | s += usage 386 | if !isZeroValue(fg, fg.DefValue) { 387 | if strings.HasSuffix(reflect.TypeOf(fg.Value).String(), "stringValue") { 388 | // put quotes on the value 389 | s += fmt.Sprintf(" (default %q)", fg.DefValue) 390 | } else { 391 | s += fmt.Sprintf(" (default %v)", fg.DefValue) 392 | } 393 | } 394 | fmt.Fprint(f.out(), s, "\n") 395 | }) 396 | } 397 | 398 | // isZeroValue guesses whether the string represents the zero 399 | // value for a flag. It is not accurate but in practice works OK. 400 | func isZeroValue(f *flag.Flag, value string) bool { 401 | // Build a zero value of the flag's Value type, and see if the 402 | // result of calling its String method equals the value passed in. 403 | // This works unless the Value type is itself an interface type. 404 | typ := reflect.TypeOf(f.Value) 405 | var z reflect.Value 406 | if typ.Kind() == reflect.Ptr { 407 | z = reflect.New(typ.Elem()) 408 | } else { 409 | z = reflect.Zero(typ) 410 | } 411 | if value == z.Interface().(flag.Value).String() { 412 | return true 413 | } 414 | 415 | switch value { 416 | case "false": 417 | return true 418 | case "": 419 | return true 420 | case "0": 421 | return true 422 | } 423 | return false 424 | } 425 | --------------------------------------------------------------------------------