getopt: init module
This commit is contained in:
parent
8290a04536
commit
2729e58eab
17 changed files with 1076 additions and 3 deletions
4
getopt/.LICENSE.~undo-tree~
Normal file
4
getopt/.LICENSE.~undo-tree~
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
(undo-tree-save-format-version . 1)
|
||||
"ea9995832c3e0239223cbc9357def034cfcdda62"
|
||||
[nil current nil nil (27067 8166 957871 684000) 0 nil]
|
||||
nil
|
||||
26
getopt/LICENSE
Normal file
26
getopt/LICENSE
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
Copyright 2019 Drew DeVault <sir@cmpwn.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
43
getopt/bool.go
Normal file
43
getopt/bool.go
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
package getopt
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type boolFlag interface {
|
||||
flag.Value
|
||||
IsBoolFlag() bool
|
||||
}
|
||||
|
||||
type boolVal bool
|
||||
|
||||
func (b *boolVal) String() string {
|
||||
return strconv.FormatBool(bool(*b))
|
||||
}
|
||||
|
||||
func (b *boolVal) Set(val string) error {
|
||||
*b = boolVal(true)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *boolVal) IsBoolFlag() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// BoolVar defines a bool flag with specified name, default value, and
|
||||
// usage string. The argument p points to a bool variable in which to
|
||||
// store the value of the flag.
|
||||
func (set *FlagSet) BoolVar(p *bool, name string, value bool, usage string) {
|
||||
*p = value
|
||||
set.Var((*boolVal)(p), name, usage)
|
||||
}
|
||||
|
||||
// Bool defines a bool flag with specified name, default value, and
|
||||
// usage string. The return value is the address of a bool variable that
|
||||
// stores the value of the flag.
|
||||
func (set *FlagSet) Bool(name string, value bool, usage string) *bool {
|
||||
var b bool
|
||||
set.BoolVar(&b, name, value, usage)
|
||||
return &b
|
||||
}
|
||||
180
getopt/commandline.go
Normal file
180
getopt/commandline.go
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
package getopt
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CommandLine is the default set of command-line flags, parsed from
|
||||
// os.Args. The top-level functions such as BoolVar, Arg, and so on are
|
||||
// wrappers for the methods of CommandLine.
|
||||
var CommandLine = NewFlagSet(os.Args[0], flag.ExitOnError)
|
||||
|
||||
// Usage prints a usage message documenting all defined command-line
|
||||
// flags to os.Stderr. It is called when an error occurs while parsing
|
||||
// flags. The function is a variable that may be changed to point to a
|
||||
// custom function. By default it prints a simple header and calls
|
||||
// PrintDefaults; for details about the format of the output and how to
|
||||
// control it, see the documentation for PrintDefaults. Custom usage
|
||||
// functions may choose to exit the program; by default exiting happens
|
||||
// anyway as the command line's error handling strategy is set to
|
||||
// ExitOnError.
|
||||
var Usage = CommandLine.Usage
|
||||
|
||||
// PrintDefaults prints, to standard error unless configured otherwise,
|
||||
// a usage message showing the default settings of all defined
|
||||
// command-line flags.
|
||||
func PrintDefaults() { CommandLine.PrintDefaults() }
|
||||
|
||||
// Arg returns the i'th command-line argument. Arg(0) is the first
|
||||
// remaining argument after flags have been processed. Arg returns an
|
||||
// empty string if the requested element does not exist.
|
||||
func Arg(i int) string { return CommandLine.Arg(i) }
|
||||
|
||||
// Args returns the non-flag command-line arguments.
|
||||
func Args() []string { return CommandLine.Args() }
|
||||
|
||||
// NArg is the number of arguments remaining after flags have been
|
||||
// processed.
|
||||
func NArg() int { return CommandLine.NArg() }
|
||||
|
||||
// NFlag returns the number of command-line flags that have been set.
|
||||
func NFlag() int { return CommandLine.NFlag() }
|
||||
|
||||
// Parsed reports whether the command-line flags have been parsed.
|
||||
func Parsed() bool { return CommandLine.Parsed() }
|
||||
|
||||
// Parse parses the command-line flags from os.Args. Must be called
|
||||
// after all flags are defined and before flags are accessed by the
|
||||
// program.
|
||||
func Parse() error { return CommandLine.Parse() }
|
||||
|
||||
// Var defines a flag with the specified name and usage string. The type
|
||||
// and value of the flag are represented by the first argument, of type
|
||||
// Value, which typically holds a user-defined implementation of Value.
|
||||
// For instance, the caller could create a flag that turns a
|
||||
// comma-separated string into a slice of strings by giving the slice
|
||||
// the methods of Value; in particular, Set would decompose the
|
||||
// comma-separated string into the slice.
|
||||
func Var(value flag.Value, name string, usage string) {
|
||||
CommandLine.Var(value, name, usage)
|
||||
}
|
||||
|
||||
// Lookup returns the Flag structure of the named flag, returning nil if
|
||||
// none exists.
|
||||
func Lookup(name string) *Flag {
|
||||
return CommandLine.Lookup(name)
|
||||
}
|
||||
|
||||
// SetOutput sets the destination for usage and error messages. If
|
||||
// output is nil, os.Stderr is used.
|
||||
func SetOutput(output io.Writer) {
|
||||
CommandLine.SetOutput(output)
|
||||
}
|
||||
|
||||
// BoolVar defines a bool flag with specified name, default value, and
|
||||
// usage string. The argument p points to a bool variable in which to
|
||||
// store the value of the flag.
|
||||
func BoolVar(p *bool, name string, value bool, usage string) {
|
||||
CommandLine.BoolVar(p, name, value, usage)
|
||||
}
|
||||
|
||||
// Bool defines a bool flag with specified name, default value, and
|
||||
// usage string. The return value is the address of a bool variable that
|
||||
// stores the value of the flag.
|
||||
func Bool(name string, value bool, usage string) *bool {
|
||||
return CommandLine.Bool(name, value, usage)
|
||||
}
|
||||
|
||||
// DurationVar defines a time.Duration flag with specified name, default
|
||||
// value, and usage string. The argument p points to a time.Duration
|
||||
// variable in which to store the value of the flag. The flag accepts a
|
||||
// value acceptable to time.ParseDuration.
|
||||
func DurationVar(p *time.Duration, name string, value time.Duration, usage string) {
|
||||
CommandLine.DurationVar(p, name, value, usage)
|
||||
}
|
||||
|
||||
// Duration defines a time.Duration flag with specified name, default
|
||||
// value, and usage string. The return value is the address of a
|
||||
// time.Duration variable that stores the value of the flag. The flag
|
||||
// accepts a value acceptable to time.ParseDuration.
|
||||
func Duration(name string, value time.Duration, usage string) *time.Duration {
|
||||
return CommandLine.Duration(name, value, usage)
|
||||
}
|
||||
|
||||
// Float64Var defines a float64 flag with specified name, default value,
|
||||
// and usage string. The argument p points to a float64 variable in
|
||||
// which to store the value of the flag.
|
||||
func Float64Var(p *float64, name string, value float64, usage string) {
|
||||
CommandLine.Float64Var(p, name, value, usage)
|
||||
}
|
||||
|
||||
// Float64 defines a float64 flag with specified name, default value,
|
||||
// and usage string. The return value is the address of a float64
|
||||
// variable that stores the value of the flag.
|
||||
func Float64(name string, value float64, usage string) *float64 {
|
||||
return CommandLine.Float64(name, value, usage)
|
||||
}
|
||||
|
||||
// IntVar defines a int flag with specified name, default value,
|
||||
// and usage string. The argument p points to a int variable in
|
||||
// which to store the value of the flag.
|
||||
func IntVar(p *int, name string, value int, usage string) {
|
||||
CommandLine.IntVar(p, name, value, usage)
|
||||
}
|
||||
|
||||
// Int defines an int flag with specified name, default value, and usage
|
||||
// string. The return value is the address of an int variable that
|
||||
// stores the value of the flag.
|
||||
func Int(name string, value int, usage string) *int {
|
||||
return CommandLine.Int(name, value, usage)
|
||||
}
|
||||
|
||||
// Int64Var defines an int64 flag with specified name, default value,
|
||||
// and usage string. The argument p points to an int64 variable in which
|
||||
// to store the value of the flag.
|
||||
func Int64Var(p *int64, name string, value int64, usage string) {
|
||||
CommandLine.Int64Var(p, name, value, usage)
|
||||
}
|
||||
|
||||
// Int64 defines an int64 flag with specified name, default value, and
|
||||
// usage string. The return value is the address of an int64 variable
|
||||
// that stores the value of the flag.
|
||||
func Int64(name string, value int64, usage string) *int64 {
|
||||
return CommandLine.Int64(name, value, usage)
|
||||
}
|
||||
|
||||
// StringVar defines a string flag with specified name, default value,
|
||||
// and usage string. The argument p points to a string variable in which
|
||||
// to store the value of the flag.
|
||||
func StringVar(p *string, name string, value string, usage string) {
|
||||
CommandLine.StringVar(p, name, value, usage)
|
||||
}
|
||||
|
||||
// String defines a string flag with specified name, default value, and
|
||||
// usage string. The return value is the address of a string variable
|
||||
// that stores the value of the flag.
|
||||
func String(name string, value string, usage string) *string {
|
||||
return CommandLine.String(name, value, usage)
|
||||
}
|
||||
|
||||
// Uint64Var defines a uint64 flag with specified name, default value,
|
||||
// and usage string. The argument p points to a uint64 variable in which
|
||||
// to store the value of the flag.
|
||||
func Uint64Var(p *uint64, name string, value uint64, usage string) {
|
||||
CommandLine.Uint64Var(p, name, value, usage)
|
||||
}
|
||||
|
||||
// Uint64 defines a uint64 flag with specified name, default value, and
|
||||
// usage string. The return value is the address of a uint64 variable
|
||||
// that stores the value of the flag.
|
||||
func Uint64(name string, value uint64, usage string) *uint64 {
|
||||
return CommandLine.Uint64(name, value, usage)
|
||||
}
|
||||
|
||||
// Set sets the value of the named flag.
|
||||
func Set(name, value string) error {
|
||||
return CommandLine.Set(name, value)
|
||||
}
|
||||
39
getopt/duration.go
Normal file
39
getopt/duration.go
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package getopt
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type durationVal time.Duration
|
||||
|
||||
func (d *durationVal) String() string {
|
||||
return time.Duration(*d).String()
|
||||
}
|
||||
|
||||
func (d *durationVal) Set(val string) error {
|
||||
v, err := time.ParseDuration(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*d = durationVal(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DurationVar defines a time.Duration flag with specified name, default
|
||||
// value, and usage string. The argument p points to a time.Duration
|
||||
// variable in which to store the value of the flag. The flag accepts a
|
||||
// value acceptable to time.ParseDuration.
|
||||
func (set *FlagSet) DurationVar(p *time.Duration, name string, value time.Duration, usage string) {
|
||||
*p = value
|
||||
set.Var((*durationVal)(p), name, usage)
|
||||
}
|
||||
|
||||
// Duration defines a time.Duration flag with specified name, default
|
||||
// value, and usage string. The return value is the address of a
|
||||
// time.Duration variable that stores the value of the flag. The flag
|
||||
// accepts a value acceptable to time.ParseDuration.
|
||||
func (set *FlagSet) Duration(name string, value time.Duration, usage string) *time.Duration {
|
||||
var d time.Duration
|
||||
set.DurationVar(&d, name, value, usage)
|
||||
return &d
|
||||
}
|
||||
74
getopt/flag_test.go
Normal file
74
getopt/flag_test.go
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
package getopt
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestInt(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
p := NewFlagSet("", 0)
|
||||
var k int
|
||||
p.IntVar(&k, "k", 16, "set k")
|
||||
i := p.Int64("i", -1, "set i")
|
||||
j := p.Uint("j", 64, "set j")
|
||||
|
||||
err := p.parse([]string{"bin", "-i", "32", "normal arg"})
|
||||
assert.Nil(err, "Expected err to be nil")
|
||||
assert.Equal(3, p.optindex, "Expected to only parse two arguments")
|
||||
assert.Equal(int64(32), *i, "Expected -i argument to equal 32")
|
||||
assert.Equal(uint(64), *j, "Expected -j argument to equal 64, since unset")
|
||||
assert.Equal(16, k, "Expected -k argument to equal 16, since unset")
|
||||
}
|
||||
|
||||
func TestBool(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
p := NewFlagSet("", 0)
|
||||
var a bool
|
||||
p.BoolVar(&a, "a", false, "set a")
|
||||
b := p.Bool("b", false, "set b")
|
||||
|
||||
err := p.parse([]string{"bin", "-a", "normal arg"})
|
||||
assert.Nil(err, "Expected err to be nil")
|
||||
assert.Equal(2, p.optindex, "Expected to only parse two arguments")
|
||||
assert.Equal(true, a, "Expected -a argument to be set")
|
||||
assert.Equal(false, *b, "Expected -b argument to not be set")
|
||||
}
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
p := NewFlagSet("", 0)
|
||||
get := p.String("c", "default", "get -c")
|
||||
|
||||
opt := "some options"
|
||||
err := p.parse([]string{"bin", "-c", opt, "normal arg"})
|
||||
assert.Nil(err, "Expected err to be nil")
|
||||
assert.Equal(opt, *get, "Expected argument to be parsed")
|
||||
}
|
||||
|
||||
func TestFloat64(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
p := NewFlagSet("", 0)
|
||||
f := p.Float64("f", -3.14, "get -f")
|
||||
|
||||
err := p.parse([]string{"bin", "-f", "3.14", "normal arg"})
|
||||
assert.Nil(err, "Expected err to be nil")
|
||||
assert.Equal(3.14, *f, "Expected -f to equal 3.14")
|
||||
}
|
||||
|
||||
func TestDuration(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
p := NewFlagSet("", 0)
|
||||
d := p.Duration("d", 0, "get -d")
|
||||
|
||||
err := p.parse([]string{"bin", "-d", "1h3m", "normal arg"})
|
||||
assert.Nil(err, "Expected err to be nil")
|
||||
assert.Equal(time.Hour+3*time.Minute, *d, "Expected -d to equal 1 hour and 3 minutes")
|
||||
}
|
||||
259
getopt/flagset.go
Normal file
259
getopt/flagset.go
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
package getopt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const (
|
||||
typeBool = iota
|
||||
typeString
|
||||
typeInt
|
||||
typeUint
|
||||
typeInt64
|
||||
typeUint64
|
||||
typeFloat64
|
||||
typeDuration
|
||||
)
|
||||
|
||||
// A Flag represents the state of a flag.
|
||||
type Flag struct {
|
||||
Name string
|
||||
Rune rune
|
||||
Value flag.Value
|
||||
Usage string
|
||||
used bool
|
||||
}
|
||||
|
||||
// A FlagSet represents a set of defined flags. The zero value of a
|
||||
// FlagSet has no name and has ContinueOnError error handling.
|
||||
type FlagSet struct {
|
||||
parsed bool
|
||||
optindex int
|
||||
|
||||
flags map[rune]*Flag
|
||||
output io.Writer
|
||||
|
||||
name string
|
||||
errorHandling flag.ErrorHandling
|
||||
|
||||
Usage func()
|
||||
}
|
||||
|
||||
// NewFlagSet returns a new, empty flag set.
|
||||
func NewFlagSet(name string, err flag.ErrorHandling) *FlagSet {
|
||||
set := FlagSet{
|
||||
name: name,
|
||||
flags: make(map[rune]*Flag),
|
||||
output: os.Stderr,
|
||||
errorHandling: err,
|
||||
}
|
||||
set.Usage = func() {
|
||||
if set.name != "" {
|
||||
fmt.Fprintf(set.output, "Usage of %s:\n\n", set.name)
|
||||
} else {
|
||||
fmt.Fprintf(set.output, "Usage:\n\n")
|
||||
}
|
||||
set.PrintDefaults()
|
||||
}
|
||||
return &set
|
||||
}
|
||||
|
||||
func (set *FlagSet) parse(args []string) error {
|
||||
var buf bytes.Buffer
|
||||
for r, f := range set.flags {
|
||||
buf.WriteRune(r)
|
||||
if f, ok := f.Value.(boolFlag); !ok || !f.IsBoolFlag() {
|
||||
buf.WriteByte(':')
|
||||
}
|
||||
}
|
||||
|
||||
options, optind, err := Getopts(args, buf.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
set.optindex = optind
|
||||
|
||||
for _, opt := range options {
|
||||
err = set.Set(fmt.Sprintf("%c", opt.Option), opt.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
set.parsed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set sets the value of the named flag.
|
||||
func (set *FlagSet) Set(name, value string) error {
|
||||
r, _ := utf8.DecodeRuneInString(name)
|
||||
flag, ok := set.flags[r]
|
||||
if !ok {
|
||||
return fmt.Errorf("no such flag -%v", name)
|
||||
}
|
||||
|
||||
flag.used = true
|
||||
return flag.Value.Set(value)
|
||||
}
|
||||
|
||||
// Arg returns the i'th command-line argument. Arg(0) is the first
|
||||
// remaining argument after flags have been processed. Arg returns an
|
||||
// empty string if the requested element does not exist.
|
||||
func (set *FlagSet) Arg(i int) string {
|
||||
var arg string
|
||||
if len(set.Args()) < i {
|
||||
arg = set.Args()[i]
|
||||
}
|
||||
return arg
|
||||
}
|
||||
|
||||
// Args returns the non-flag command-line arguments.
|
||||
func (set *FlagSet) Args() []string {
|
||||
return os.Args[set.optindex:]
|
||||
}
|
||||
|
||||
// NArg is the number of arguments remaining after flags have been
|
||||
// processed.
|
||||
func (set *FlagSet) NArg() int {
|
||||
return len(set.Args())
|
||||
}
|
||||
|
||||
// NFlag returns the number of command-line flags that have been set.
|
||||
func (set *FlagSet) NFlag() int {
|
||||
return len(set.flags)
|
||||
}
|
||||
|
||||
// Parsed reports whether the command-line flags have been parsed.
|
||||
func (set *FlagSet) Parsed() bool {
|
||||
return set.parsed
|
||||
}
|
||||
|
||||
// ParseSlice parses the command-line flags from args. Must be called
|
||||
// after all flags are defined and before flags are accessed by the
|
||||
// program.
|
||||
func (set *FlagSet) ParseSlice(args []string) (err error) {
|
||||
err = set.parse(args)
|
||||
if err != nil {
|
||||
switch set.errorHandling {
|
||||
case flag.PanicOnError:
|
||||
panic(err)
|
||||
case flag.ExitOnError:
|
||||
fmt.Println(err)
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Parse parses the command-line flags from os.Args. Must be called
|
||||
// after all flags are defined and before flags are accessed by the
|
||||
// program.
|
||||
func (set *FlagSet) Parse() error {
|
||||
return set.ParseSlice(os.Args)
|
||||
}
|
||||
|
||||
// ErrorHandling returns the error handling behavior of the flag set.
|
||||
func (set *FlagSet) ErrorHandling() flag.ErrorHandling {
|
||||
return set.errorHandling
|
||||
}
|
||||
|
||||
// Var defines a flag with the specified name and usage string. The type
|
||||
// and value of the flag are represented by the first argument, of type
|
||||
// Value, which typically holds a user-defined implementation of Value.
|
||||
// For instance, the caller could create a flag that turns a
|
||||
// comma-separated string into a slice of strings by giving the slice
|
||||
// the methods of Value; in particular, Set would decompose the
|
||||
// comma-separated string into the slice.
|
||||
func (set *FlagSet) Var(value flag.Value, name string, usage string) {
|
||||
r, _ := utf8.DecodeRuneInString(name)
|
||||
set.flags[r] = &Flag{
|
||||
Name: name,
|
||||
Rune: r,
|
||||
Value: value,
|
||||
Usage: usage,
|
||||
}
|
||||
}
|
||||
|
||||
func (set *FlagSet) visitIf(cond func(*Flag) bool, fn func(*Flag)) {
|
||||
var runes []rune
|
||||
for r := range set.flags {
|
||||
runes = append(runes, r)
|
||||
}
|
||||
|
||||
for i := range runes {
|
||||
for j := i; j > 0; j-- {
|
||||
if runes[j] < runes[j-1] {
|
||||
runes[j], runes[j-1] = runes[j-1], runes[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, r := range runes {
|
||||
if cond == nil || cond(set.flags[r]) {
|
||||
fn(set.flags[r])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// VisitAll visits the command-line flags in lexicographical order,
|
||||
// calling fn for each. It visits all flags, even those not set.
|
||||
func (set *FlagSet) VisitAll(fn func(*Flag)) {
|
||||
set.visitIf(nil, fn)
|
||||
}
|
||||
|
||||
// Visit visits the command-line flags in lexicographical order,
|
||||
// calling fn for each.
|
||||
func (set *FlagSet) Visit(fn func(*Flag)) {
|
||||
set.visitIf(func(flag *Flag) bool { return flag.used }, fn)
|
||||
}
|
||||
|
||||
// Lookup returns the Flag structure of the named flag, returning nil if
|
||||
// none exists.
|
||||
func (set *FlagSet) Lookup(name string) *Flag {
|
||||
r, _ := utf8.DecodeRuneInString(name)
|
||||
return set.flags[r]
|
||||
}
|
||||
|
||||
// PrintDefaults prints, to standard error unless configured otherwise,
|
||||
// a usage message showing the default settings of all defined
|
||||
// command-line flags.
|
||||
func (set *FlagSet) PrintDefaults() {
|
||||
out := set.output
|
||||
set.VisitAll(func(flag *Flag) {
|
||||
fmt.Fprintf(out, " -%c", flag.Rune)
|
||||
|
||||
val := flag.Value
|
||||
|
||||
usage := strings.Replace(flag.Usage, "\n", "\n \t", -1)
|
||||
fmt.Fprintf(out, "\t%s", usage)
|
||||
|
||||
if _, ok := val.(*stringVal); ok {
|
||||
fmt.Fprintf(out, " (default %q)", val)
|
||||
} else if _, ok := val.(*boolVal); !ok {
|
||||
fmt.Fprintf(out, " (default %s)", val)
|
||||
}
|
||||
fmt.Fprintf(out, "\n")
|
||||
})
|
||||
}
|
||||
|
||||
// SetOutput sets the destination for usage and error messages. If
|
||||
// output is nil, os.Stderr is used.
|
||||
func (set *FlagSet) SetOutput(output io.Writer) {
|
||||
if output == nil {
|
||||
set.output = os.Stderr
|
||||
} else {
|
||||
set.output = output
|
||||
}
|
||||
}
|
||||
|
||||
// Output returns the destination for usage and error messages.
|
||||
// os.Stderr is returned if output was not set or was set to nil.
|
||||
func (set *FlagSet) Output() io.Writer {
|
||||
return set.output
|
||||
}
|
||||
33
getopt/float.go
Normal file
33
getopt/float.go
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package getopt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type floatVal float64
|
||||
|
||||
func (f *floatVal) String() string {
|
||||
return fmt.Sprint(float64(*f))
|
||||
}
|
||||
|
||||
func (f *floatVal) Set(val string) error {
|
||||
_, err := fmt.Sscanf(val, "%g", f)
|
||||
return err
|
||||
}
|
||||
|
||||
// Float64Var defines a float64 flag with specified name, default value,
|
||||
// and usage string. The argument p points to a float64 variable in
|
||||
// which to store the value of the flag.
|
||||
func (set *FlagSet) Float64Var(p *float64, name string, value float64, usage string) {
|
||||
*p = value
|
||||
set.Var((*floatVal)(p), name, usage)
|
||||
}
|
||||
|
||||
// Float64 defines a float64 flag with specified name, default value,
|
||||
// and usage string. The return value is the address of a float64
|
||||
// variable that stores the value of the flag.
|
||||
func (set *FlagSet) Float64(name string, value float64, usage string) *float64 {
|
||||
var f float64
|
||||
set.Float64Var(&f, name, value, usage)
|
||||
return &f
|
||||
}
|
||||
155
getopt/getopt.go
Normal file
155
getopt/getopt.go
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
// getopt is a POSIX-compatible implementation of getopt(3) for Go.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// import (
|
||||
// "os"
|
||||
// "git.sr.ht/~sircmpwn/getopt"
|
||||
// )
|
||||
//
|
||||
// func main() {
|
||||
// opts, optind, err := getopt.Getopts(os.Args, "abc:d:")
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// for _, opt := range opts {
|
||||
// switch opt.Option {
|
||||
// case 'a':
|
||||
// println("Option -a specified")
|
||||
// case 'b':
|
||||
// println("Option -b specified")
|
||||
// case 'c':
|
||||
// println("Option -c specified: " + opt.Value)
|
||||
// case 'd':
|
||||
// println("Option -d specified: " + opt.Value)
|
||||
// }
|
||||
// }
|
||||
// println("Remaining arguments:")
|
||||
// for _, arg := range os.Args[optind:] {
|
||||
// println(arg)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// A flag[0]-like interface is also supported.
|
||||
//
|
||||
// import (
|
||||
// "git.sr.ht/~sircmpwn/getopt"
|
||||
// )
|
||||
//
|
||||
// func main() {
|
||||
// a := getopt.Bool("a", false, "turn on option a")
|
||||
// b := getopt.Int("b", 1, "set b to a numerical value")
|
||||
// var opt string
|
||||
// getopt.StringVar(&opt, "c", "", "let c be specified string")
|
||||
//
|
||||
// err := getopt.Parse()
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
//
|
||||
// print("Value of a: ")
|
||||
// println(*a)
|
||||
// print("Value of b: ")
|
||||
// println(*b)
|
||||
// println("Value of c: " + opt)
|
||||
//
|
||||
// println("Remaining arguments:")
|
||||
// for _, arg := range getopt.Args() {
|
||||
// println(arg)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// [0]: https://golang.org/pkg/flag/
|
||||
package getopt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// In the case of "-o example", Option is 'o' and "example" is Value. For
|
||||
// options which do not take an argument, Value is "".
|
||||
type Option struct {
|
||||
Option rune
|
||||
Value string
|
||||
}
|
||||
|
||||
// This is returned when an unknown option is found in argv, but not in the
|
||||
// option spec.
|
||||
type UnknownOptionError rune
|
||||
|
||||
func (e UnknownOptionError) Error() string {
|
||||
return fmt.Sprintf("%s: unknown option -%c", os.Args[0], rune(e))
|
||||
}
|
||||
|
||||
// This is returned when an option with a mandatory argument is missing that
|
||||
// argument.
|
||||
type MissingOptionError rune
|
||||
|
||||
func (e MissingOptionError) Error() string {
|
||||
return fmt.Sprintf("%s: expected argument for -%c", os.Args[0], rune(e))
|
||||
}
|
||||
|
||||
// Getopts implements a POSIX-compatible options interface.
|
||||
//
|
||||
// Returns a slice of options and the index of the first non-option argument.
|
||||
//
|
||||
// If an error is returned, you must print it to stderr to be POSIX complaint.
|
||||
func Getopts(argv []string, spec string) ([]Option, int, error) {
|
||||
optmap := make(map[rune]bool)
|
||||
runes := []rune(spec)
|
||||
for i, rn := range spec {
|
||||
if rn == ':' {
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
optmap[runes[i-1]] = true
|
||||
} else {
|
||||
optmap[rn] = false
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
i int
|
||||
opts []Option
|
||||
)
|
||||
for i = 1; i < len(argv); i++ {
|
||||
arg := argv[i]
|
||||
runes = []rune(arg)
|
||||
if len(arg) == 0 || arg == "-" {
|
||||
break
|
||||
}
|
||||
if arg[0] != '-' {
|
||||
break
|
||||
}
|
||||
if arg == "--" {
|
||||
i++
|
||||
break
|
||||
}
|
||||
for j, opt := range runes[1:] {
|
||||
if optopt, ok := optmap[opt]; !ok {
|
||||
opts = append(opts, Option{'?', ""})
|
||||
return opts, i, UnknownOptionError(opt)
|
||||
} else if optopt {
|
||||
if j+1 < len(runes)-1 {
|
||||
opts = append(opts, Option{opt, string(runes[j+2:])})
|
||||
break
|
||||
} else {
|
||||
if i+1 >= len(argv) {
|
||||
if len(spec) >= 1 && spec[0] == ':' {
|
||||
opts = append(opts, Option{':', string(opt)})
|
||||
} else {
|
||||
return opts, i, MissingOptionError(opt)
|
||||
}
|
||||
} else {
|
||||
opts = append(opts, Option{opt, argv[i+1]})
|
||||
i++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
opts = append(opts, Option{opt, ""})
|
||||
}
|
||||
}
|
||||
}
|
||||
return opts, i, nil
|
||||
}
|
||||
89
getopt/getopt_test.go
Normal file
89
getopt/getopt_test.go
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
package getopt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSimpleCase(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
opts, i, err := Getopts([]string{
|
||||
"test_bin", "-afo", "output-file", "normal arg"}, "afo:")
|
||||
assert.Nil(err, "Expected err to be nil")
|
||||
assert.Equal(len(opts), 3, "Expected 3 options to be parsed")
|
||||
assert.Equal(i, 3, "Expected non-option args to start at index 2")
|
||||
assert.Equal(opts[0], Option{'a', ""})
|
||||
assert.Equal(opts[1], Option{'f', ""})
|
||||
assert.Equal(opts[2], Option{'o', "output-file"})
|
||||
}
|
||||
|
||||
func TestShortFormArgument(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
opts, i, err := Getopts([]string{
|
||||
"test_bin", "-afooutput-file", "normal arg"}, "afo:")
|
||||
assert.Nil(err, "Expected err to be nil")
|
||||
assert.Equal(len(opts), 3, "Expected 3 options to be parsed")
|
||||
assert.Equal(i, 2, "Expected non-option args to start at index 2")
|
||||
assert.Equal(opts[0], Option{'a', ""})
|
||||
assert.Equal(opts[1], Option{'f', ""})
|
||||
assert.Equal(opts[2], Option{'o', "output-file"})
|
||||
}
|
||||
|
||||
func TestSeparateArgs(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
opts, i, err := Getopts([]string{
|
||||
"test_bin", "-a", "-f", "-o", "output-file", "normal arg"}, "afo:")
|
||||
assert.Nil(err, "Expected err to be nil")
|
||||
assert.Equal(len(opts), 3, "Expected 3 options to be parsed")
|
||||
assert.Equal(i, 5, "Expected non-option args to start at index 5")
|
||||
assert.Equal(opts[0], Option{'a', ""})
|
||||
assert.Equal(opts[1], Option{'f', ""})
|
||||
assert.Equal(opts[2], Option{'o', "output-file"})
|
||||
}
|
||||
|
||||
func TestTwoDashes(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
opts, i, err := Getopts([]string{
|
||||
"test_bin", "-afo", "output-file", "--", "-f", "normal arg"}, "afo:")
|
||||
assert.Nil(err, "Expected err to be nil")
|
||||
assert.Equal(len(opts), 3, "Expected 3 options to be parsed")
|
||||
assert.Equal(i, 4, "Expected non-option args to start at index 4")
|
||||
assert.Equal(opts[0], Option{'a', ""})
|
||||
assert.Equal(opts[1], Option{'f', ""})
|
||||
assert.Equal(opts[2], Option{'o', "output-file"})
|
||||
}
|
||||
|
||||
func TestUnknownOption(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
_, _, err := Getopts([]string{"test_bin", "-x"}, "y")
|
||||
var errt UnknownOptionError
|
||||
assert.IsType(err, errt, "Expected unknown option error")
|
||||
assert.Equal(err.Error(), fmt.Sprintf("%s: unknown option -x", os.Args[0]),
|
||||
"Expected POSIX-compatible error message")
|
||||
}
|
||||
|
||||
func TestMissingOption(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
_, _, err := Getopts([]string{"test_bin", "-x"}, "x:")
|
||||
var errt MissingOptionError
|
||||
assert.IsType(err, errt, "Expected missing option error")
|
||||
assert.Equal(err.Error(), fmt.Sprintf("%s: expected argument for -x",
|
||||
os.Args[0]), "Expected POSIX-compatible error message")
|
||||
}
|
||||
|
||||
func TestExpectedMissingOption(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
opts, _, err := Getopts([]string{"test_bin", "-x"}, ":x:")
|
||||
assert.Nil(err, "Expected err to be nil")
|
||||
assert.Equal(len(opts), 1, "Expected 1 option to be parsed")
|
||||
assert.Equal(opts[0], Option{':', "x"})
|
||||
}
|
||||
|
||||
func TestNoOption(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
_, i, _ := Getopts([]string{"test_bin"}, "")
|
||||
assert.Equal(i, 1, "Expected non-option args to start at index 1")
|
||||
}
|
||||
3
getopt/go.mod
Normal file
3
getopt/go.mod
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
module git.baxters.nz/jeremy/cli/getopt
|
||||
|
||||
require github.com/stretchr/testify v1.3.0
|
||||
7
getopt/go.sum
Normal file
7
getopt/go.sum
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
61
getopt/int.go
Normal file
61
getopt/int.go
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
package getopt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type intVal int
|
||||
|
||||
func (i *intVal) String() string {
|
||||
return fmt.Sprint(*i)
|
||||
}
|
||||
|
||||
func (i *intVal) Set(val string) error {
|
||||
_, err := fmt.Sscanf(val, "%d", i)
|
||||
return err
|
||||
}
|
||||
|
||||
// IntVar defines a int flag with specified name, default value,
|
||||
// and usage string. The argument p points to a int variable in
|
||||
// which to store the value of the flag.
|
||||
func (set *FlagSet) IntVar(p *int, name string, value int, usage string) {
|
||||
*p = value
|
||||
set.Var((*intVal)(p), name, usage)
|
||||
}
|
||||
|
||||
// Int defines an int flag with specified name, default value, and usage
|
||||
// string. The return value is the address of an int variable that
|
||||
// stores the value of the flag.
|
||||
func (set *FlagSet) Int(name string, value int, usage string) *int {
|
||||
p := new(int)
|
||||
set.IntVar(p, name, value, usage)
|
||||
return p
|
||||
}
|
||||
|
||||
type int64Val int64
|
||||
|
||||
func (i *int64Val) String() string {
|
||||
return fmt.Sprint(int64(*i))
|
||||
}
|
||||
|
||||
func (i *int64Val) Set(val string) error {
|
||||
_, err := fmt.Sscanf(val, "%d", i)
|
||||
return err
|
||||
}
|
||||
|
||||
// Int64Var defines an int64 flag with specified name, default value,
|
||||
// and usage string. The argument p points to an int64 variable in which
|
||||
// to store the value of the flag.
|
||||
func (set *FlagSet) Int64Var(p *int64, name string, value int64, usage string) {
|
||||
*p = value
|
||||
set.Var((*int64Val)(p), name, usage)
|
||||
}
|
||||
|
||||
// Int64 defines an int64 flag with specified name, default value, and
|
||||
// usage string. The return value is the address of an int64 variable
|
||||
// that stores the value of the flag.
|
||||
func (set *FlagSet) Int64(name string, value int64, usage string) *int64 {
|
||||
var i int64
|
||||
set.Int64Var(&i, name, value, usage)
|
||||
return &i
|
||||
}
|
||||
29
getopt/string.go
Normal file
29
getopt/string.go
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
package getopt
|
||||
|
||||
type stringVal string
|
||||
|
||||
func (s *stringVal) String() string {
|
||||
return string(*s)
|
||||
}
|
||||
|
||||
func (s *stringVal) Set(val string) error {
|
||||
*s = stringVal(val)
|
||||
return nil
|
||||
}
|
||||
|
||||
// StringVar defines a string flag with specified name, default value,
|
||||
// and usage string. The argument p points to a string variable in which
|
||||
// to store the value of the flag.
|
||||
func (set *FlagSet) StringVar(p *string, name string, value string, usage string) {
|
||||
*p = value
|
||||
set.Var((*stringVal)(p), name, usage)
|
||||
}
|
||||
|
||||
// String defines a string flag with specified name, default value, and
|
||||
// usage string. The return value is the address of a string variable
|
||||
// that stores the value of the flag.
|
||||
func (set *FlagSet) String(name string, value string, usage string) *string {
|
||||
var s string
|
||||
set.StringVar(&s, name, value, usage)
|
||||
return &s
|
||||
}
|
||||
73
getopt/uint.go
Normal file
73
getopt/uint.go
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
package getopt
|
||||
|
||||
import "fmt"
|
||||
|
||||
type uintVal uint
|
||||
|
||||
func (i *uintVal) String() string {
|
||||
return fmt.Sprint(uint(*i))
|
||||
}
|
||||
|
||||
func (i *uintVal) Set(val string) error {
|
||||
_, err := fmt.Sscanf(val, "%d", i)
|
||||
return err
|
||||
}
|
||||
|
||||
// UintVar defines a uint flag with specified name, default value, and
|
||||
// usage string. The argument p points to a uint variable in which to
|
||||
// store the value of the flag.
|
||||
func (set *FlagSet) UintVar(p *uint, name string, value uint, usage string) {
|
||||
*p = value
|
||||
set.Var((*uintVal)(p), name, usage)
|
||||
}
|
||||
|
||||
// UintVar defines a uint flag with specified name, default value, and
|
||||
// usage string. The argument p points to a uint variable in which to
|
||||
// store the value of the flag.
|
||||
func UintVar(p *uint, name string, value uint, usage string) {
|
||||
CommandLine.UintVar(p, name, value, usage)
|
||||
}
|
||||
|
||||
// Uint defines a uint flag with specified name, default value, and
|
||||
// usage string. The return value is the address of a uint variable that
|
||||
// stores the value of the flag.
|
||||
func (set *FlagSet) Uint(name string, value uint, usage string) *uint {
|
||||
var b uint
|
||||
set.UintVar(&b, name, value, usage)
|
||||
return &b
|
||||
}
|
||||
|
||||
// Uint defines a uint flag with specified name, default value, and
|
||||
// usage string. The return value is the address of a uint variable that
|
||||
// stores the value of the flag.
|
||||
func Uint(name string, value uint, usage string) *uint {
|
||||
return CommandLine.Uint(name, value, usage)
|
||||
}
|
||||
|
||||
type uint64Val uint64
|
||||
|
||||
func (i *uint64Val) String() string {
|
||||
return fmt.Sprint(uint64(*i))
|
||||
}
|
||||
|
||||
func (i *uint64Val) Set(val string) error {
|
||||
_, err := fmt.Sscanf(val, "%d", i)
|
||||
return err
|
||||
}
|
||||
|
||||
// Uint64Var defines a uint64 flag with specified name, default value,
|
||||
// and usage string. The argument p points to a uint64 variable in which
|
||||
// to store the value of the flag.
|
||||
func (set *FlagSet) Uint64Var(p *uint64, name string, value uint64, usage string) {
|
||||
*p = value
|
||||
set.Var((*uint64Val)(p), name, usage)
|
||||
}
|
||||
|
||||
// Uint64 defines a uint64 flag with specified name, default value, and
|
||||
// usage string. The return value is the address of a uint64 variable
|
||||
// that stores the value of the flag.
|
||||
func (set *FlagSet) Uint64(name string, value uint64, usage string) *uint64 {
|
||||
var b uint64
|
||||
set.Uint64Var(&b, name, value, usage)
|
||||
return &b
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue