From b7d65b758775a7c9c74098602555f2d1454c2633 Mon Sep 17 00:00:00 2001 From: "Sean E. Russell" Date: Mon, 27 Apr 2020 13:53:45 -0500 Subject: [PATCH] Change from docopt to pflag to support extensions having options --- cmd/gotop/main.go | 250 +++++++++++++++++++-------------------------- devices/devices.go | 20 +++- go.mod | 5 + go.sum | 43 ++++++++ 4 files changed, 174 insertions(+), 144 deletions(-) diff --git a/cmd/gotop/main.go b/cmd/gotop/main.go index 0b08846..d8e0493 100644 --- a/cmd/gotop/main.go +++ b/cmd/gotop/main.go @@ -9,15 +9,16 @@ import ( "os/signal" "path/filepath" "sort" - "strconv" "strings" "syscall" "time" - docopt "github.com/docopt/docopt.go" + //_ "net/http/pprof" + ui "github.com/gizak/termui/v3" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/shibukawa/configdir" + flag "github.com/xxxserxxx/pflag" "github.com/xxxserxxx/gotop/v4" "github.com/xxxserxxx/gotop/v4/colorschemes" @@ -48,127 +49,66 @@ var ( stderrLogger = log.New(os.Stderr, "", 0) ) -// TODO: Add tab completion for Linux https://gist.github.com/icholy/5314423 -// TODO: state:merge #135 linux console font (cmatsuoka/console-font) -// TODO: Abstract out the UI toolkit. mum4k/termdash, VladimirMarkelov/clui, gcla/gowid, rivo/tview, marcusolsson/tui-go might work better for some OS/Archs. Performance/memory use comparison would be interesting. func parseArgs(conf *gotop.Config) error { cds := conf.ConfigDir.QueryFolders(configdir.All) cpaths := make([]string, len(cds)) for i, p := range cds { cpaths[i] = p.Path } - usage := fmt.Sprintln(` -Usage: gotop [options] - -Options: - -c, --color=NAME Set a colorscheme. - -h, --help Show this screen. - -S, --graphscale=INT Graph scale factor, from 1+ [default: 7] - -r, --rate=RATE Number of times per second to update CPU and Mem widgets [default: 1]. - -V, --version Print version and exit. - -p, --percpu Show each CPU in the CPU widget. - -a, --averagecpu Show average CPU in the CPU widget. - -f, --fahrenheit Show temperatures in fahrenheit. - -s, --statusbar Show a statusbar with the time. - -B, --bandwidth=bits Specify the number of bits per seconds. - -l, --layout=NAME Name of layout spec file for the UI. Use "-" to pipe. - -i, --interface=NAME Select network interface [default: all]. Several interfaces can be defined using comma separated values. Interfaces can also be ignored using ! - -x, --export=PORT Enable metrics for export on the specified port. - --mbps Show mbps for network IO. - --test Runs tests and exits with success/failure code. - --list - devices: Prints out device names for widgets supporting filters. - layouts: Lists build-in layouts - colorschemes: Lists built-in colorschemes - paths: List out configuration file search paths - keys: Show the keyboard bindings. - --write-config Write out a default config file.`) - - args, err := docopt.ParseArgs(usage, os.Args[1:], Version) + help := flag.BoolP("help", "h", false, "Show this screen.") + color := flag.StringP("color", "c", conf.Colorscheme.Name, "Set a colorscheme.") + flag.IntVarP(&conf.GraphHorizontalScale, "graphscale", "S", conf.GraphHorizontalScale, "Graph scale factor, >0") + version := flag.BoolP("version", "v", false, "Print version and exit.") + versioN := flag.BoolP("", "V", false, "Print version and exit.") + flag.BoolVarP(&conf.PercpuLoad, "percpu", "p", conf.PercpuLoad, "Show each CPU in the CPU widget.") + flag.BoolVarP(&conf.AverageLoad, "averagecpu", "a", conf.AverageLoad, "Show average CPU in the CPU widget.") + fahrenheit := flag.BoolP("fahrenheit", "f", conf.TempScale == 'F', "Show temperatures in fahrenheit.Show temperatures in fahrenheit.") + flag.BoolVarP(&conf.Statusbar, "statusbar", "s", conf.Statusbar, "Show a statusbar with the time.") + flag.DurationVarP(&conf.UpdateInterval, "rate", "r", conf.UpdateInterval, "Number of times per second to update CPU and Mem widgets.") + flag.StringVarP(&conf.ExportPort, "layout", "l", conf.Layout, `Name of layout spec file for the UI. Use "-" to pipe.`) + flag.StringVarP(&conf.NetInterface, "interface", "i", "all", "Select network interface. Several interfaces can be defined using comma separated values. Interfaces can also be ignored using `!`") + flag.StringVarP(&conf.ExportPort, "export", "x", conf.ExportPort, "Enable metrics for export on the specified port.") + flag.BoolVarP(&conf.Mbps, "mbps", "", conf.Mbps, "Show network rate as mbps.") + // FIXME Where did this go?? + //conf.Band = flag.IntP("bandwidth", "B", 100, "Specify the number of bits per seconds.") + flag.BoolVar(&conf.Test, "test", conf.Test, "Runs tests and exits with success/failure code.") + list := flag.String("list", "", `List +devices: Prints out device names for filterable widgets +layouts: Lists build-in layouts +colorschemes: Lists built-in colorschemes +paths: List out configuration file search paths +keys: Show the keyboard bindings.`) + wc := flag.Bool("write-config", false, "Write out a default config file.") + flag.CommandLine.SortFlags = false + flag.Usage = func() { + fmt.Fprintf(os.Stderr, "Usage: %s [options]\n\nOptions:\n", os.Args[0]) + flag.PrintDefaults() + } + flag.Parse() + if *version || *versioN { + fmt.Printf("gotop %s (%s)\n", Version, BuildDate) + os.Exit(0) + } + if *help { + flag.Usage() + os.Exit(0) + } + cs, err := colorschemes.FromName(conf.ConfigDir, *color) if err != nil { return err } - - if val, _ := args["--layout"]; val != nil { - conf.Layout = val.(string) + conf.Colorscheme = cs + if *fahrenheit { + conf.TempScale = 'F' + } else { + conf.TempScale = 'C' } - if val, _ := args["--color"]; val != nil { - cs, err := colorschemes.FromName(conf.ConfigDir, val.(string)) - if err != nil { - return err - } - conf.Colorscheme = cs - } - - if args["--averagecpu"].(bool) { - conf.AverageLoad, _ = args["--averagecpu"].(bool) - } - if args["--percpu"].(bool) { - conf.PercpuLoad, _ = args["--percpu"].(bool) - } - if args["--statusbar"].(bool) { - statusbar, _ = args["--statusbar"].(bool) - } - if val, _ := args["--export"]; val != nil { - conf.ExportPort = val.(string) - } - if val, _ := args["--rate"]; val != nil { - rateStr, _ := val.(string) - rate, err := strconv.ParseFloat(rateStr, 64) - if err != nil { - return fmt.Errorf("invalid rate parameter") - } - if rate < 1 { - conf.UpdateInterval = time.Second * time.Duration(1/rate) - } else { - conf.UpdateInterval = time.Second / time.Duration(rate) - } - } - if val, _ := args["--fahrenheit"]; val != nil { - fahrenheit, _ := val.(bool) - if fahrenheit { - conf.TempScale = w.Fahrenheit - } - } - if val, _ := args["--interface"]; val != nil { - conf.NetInterface, _ = args["--interface"].(string) - } - if val, _ := args["--test"]; val != nil { - conf.Test = val.(bool) - } - if val, _ := args["--graphscale"]; val != nil { - str, _ := args["--graphscale"].(string) - scl, err := strconv.Atoi(str) - if err != nil { - fmt.Printf("invalid value \"%s\" for graphscale; must be an integer\n", args["--graphscale"]) - os.Exit(1) - } - if scl < 1 { - fmt.Printf("graphscale must be greater than 0 [1, ∞); you provided %d. Values > 30 are probably not useful.\n", scl) - os.Exit(1) - } - conf.GraphHorizontalScale = scl - } - if args["--mbps"].(bool) { - conf.Mbps = true - } - if val, _ := args["--list"]; val != nil { - switch val { + if *list != "" { + switch *list { case "layouts": - fmt.Println("Built-in layouts:") - fmt.Println("\tdefault") - fmt.Println("\tminimal") - fmt.Println("\tbattery") - fmt.Println("\tkitchensink") + fmt.Println(LAYOUTS) case "colorschemes": - fmt.Println("Built-in colorschemes:") - fmt.Println("\tdefault") - fmt.Println("\tdefault-dark (for white background)") - fmt.Println("\tsolarized") - fmt.Println("\tsolarized16-dark") - fmt.Println("\tsolarized16-light") - fmt.Println("\tmonokai") - fmt.Println("\tvice") + fmt.Println(COLORSCHEMES) case "paths": fmt.Println("Loadable colorschemes & layouts, and the config file, are searched for, in order:") paths := make([]string, 0) @@ -180,42 +120,14 @@ Options: case "devices": listDevices() case "keys": - fmt.Println(` -Quit: q or -Process navigation: - k and : up - j and : down - : half page up - : half page down - : full page up - : full page down - gg and : jump to top - G and : jump to bottom -Process actions: - : toggle process grouping - dd: kill selected process or group of processes with SIGTERM - d3: kill selected process or group of processes with SIGQUIT - d9: kill selected process or group of processes with SIGKILL -Process sorting - c: CPU - m: Mem - p: PID -Process filtering: - /: start editing filter - (while editing): - accept filter - and : clear filter -CPU and Mem graph scaling: - h: scale in - l: scale out -?: toggles keybind help menu`) + fmt.Println(KEYS) default: - fmt.Printf("Unknown option \"%s\"; try layouts, colorschemes, or devices\n", val) + fmt.Printf("Unknown option \"%s\"; try layouts, colorschemes, or devices\n", *list) os.Exit(1) } os.Exit(0) } - if args["--write-config"].(bool) { + if *wc { path, err := conf.Write() if err != nil { fmt.Printf("Failed to write configuration file: %s\n", err) @@ -224,7 +136,6 @@ CPU and Mem graph scaling: fmt.Printf("Config written to %s\n", path) os.Exit(0) } - return nil } @@ -438,7 +349,17 @@ func makeConfig() gotop.Config { // TODO: Add fans // TODO: mpd visualizer widget +// TODO: Add tab completion for Linux https://gist.github.com/icholy/5314423 +// TODO: state:merge #135 linux console font (cmatsuoka/console-font) +// TODO: Abstract out the UI toolkit. mum4k/termdash, VladimirMarkelov/clui, gcla/gowid, rivo/tview, marcusolsson/tui-go might work better for some OS/Archs. Performance/memory use comparison would be interesting. +// TODO: all of the go vet stuff, more unit tests, benchmarks, finish remote. +// TODO: color bars for memory, a-la bashtop func main() { + // For performance testing + //go func() { + // log.Fatal(http.ListenAndServe(":7777", nil)) + //}() + // This is just to make sure gotop returns a useful exit code, but also // executes all defer statements and so cleans up before exit. Sort of // annoying work-around for a lack of a clean way to exit Go programs @@ -486,6 +407,7 @@ func run() int { return runTests(conf) } + //devices.Startup(conf) if err := ui.Init(); err != nil { stderrLogger.Print(err) return 1 @@ -575,3 +497,45 @@ func listDevices() { } } } + +const KEYS = `Quit: q or +Process navigation: + k and : up + j and : down + : half page up + : half page down + : full page up + : full page down + gg and : jump to top + G and : jump to bottom +Process actions: + : toggle process grouping + dd: kill selected process or group of processes with SIGTERM + d3: kill selected process or group of processes with SIGQUIT + d9: kill selected process or group of processes with SIGKILL +Process sorting + c: CPU + m: Mem + p: PID +Process filtering: + /: start editing filter + (while editing): + accept filter + and : clear filter +CPU and Mem graph scaling: + h: scale in + l: scale out +?: toggles keybind help menu` +const LAYOUTS = `Built-in layouts: +\tdefault +\tminimal +\tbattery +\tkitchensink` +const COLORSCHEMES = `Built-in colorschemes: +\tdefault +\tdefault-dark (for white background) +\tsolarized +\tsolarized16-dark +\tsolarized16-light +\tmonokai +\tvice` diff --git a/devices/devices.go b/devices/devices.go index 4a029cd..81b0521 100644 --- a/devices/devices.go +++ b/devices/devices.go @@ -1,6 +1,8 @@ package devices -import "log" +import ( + "log" +) const ( Temperatures = "Temperatures" // Device domain for temperature sensors @@ -20,6 +22,22 @@ func RegisterShutdown(f func() error) { _shutdownFuncs = append(_shutdownFuncs, f) } +/* +func RegisterStartup(f func(gotop.Config)) { + if _startup == nil { + _startup = make([]func(gotop.Config), 1) + } + _startup = append(_startup, f) +} + +// Called after configuration has been parsed +func Startup(c gotop.Config) { + for _, f := range _startup { + f(c) + } +} +*/ + // Shutdown will be called by the `main()` function if gotop is exited // cleanly. It will call all of the registered shutdown functions of devices, // logging all errors but otherwise not responding to them. diff --git a/go.mod b/go.mod index d144b3b..97508d5 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,12 @@ require ( github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect github.com/stretchr/testify v1.4.0 github.com/xxxserxxx/iSMC v1.0.1 + github.com/xxxserxxx/pflag v1.0.8 + golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a // indirect + golang.org/x/tools v0.0.0-20200425043458-8463f397d07c // indirect + golang.org/x/tools/gopls v0.4.0 // indirect howett.net/plist v0.0.0-20181124034731-591f970eefbb // indirect + mvdan.cc/xurls/v2 v2.2.0 // indirect ) go 1.14 diff --git a/go.sum b/go.sum index c4064cf..4a2c105 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -37,10 +39,12 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= @@ -85,6 +89,10 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 h1:Xuk8ma/ibJ1fOy4Ee11vHhUFHQNpHhrBneOCNHVXS5w= github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0/go.mod h1:7AwjWCpdPhkSmNAgUv5C7EJ4AbmjEB3r047r3DXWu3Y= github.com/shirou/gopsutil v2.18.11+incompatible h1:PMFTKnFTr/YTRW5rbLK4vWALV3a+IGXse5nvhSjztmg= @@ -105,22 +113,50 @@ github.com/xxxserxxx/gotop/v3 v3.5.1 h1:aBf++Oxg7qCZpKqYpPPnXKFOxT1KYLPtiEXRh57y github.com/xxxserxxx/gotop/v3 v3.5.1/go.mod h1:DGPTiAmUhqE21xvokK64BuMxW+EmnOptaxpdOlqiH6s= github.com/xxxserxxx/iSMC v1.0.1 h1:M9Gkwnnkl+evvnugoB5yRYrbUP+cRIVOPM+xrHZc3Hs= github.com/xxxserxxx/iSMC v1.0.1/go.mod h1:TGgNjU7BF2DZSuxiTft+BdzxzcujFJYqFfMCzcTl/aY= +github.com/xxxserxxx/pflag v1.0.7 h1:451NW+tmkc1R30NnibaAbbsysLqGLguFrdLr6SIbYac= +github.com/xxxserxxx/pflag v1.0.7/go.mod h1:Nyfo0BBT+1wKxL37phQvEyr5zgzmQ+C7/CiXSOsYR2w= +github.com/xxxserxxx/pflag v1.0.8 h1:UuFim2L1eNWapysJARSJ+QbbUOxEz23VrYWe4mFXTg8= +github.com/xxxserxxx/pflag v1.0.8/go.mod h1:Nyfo0BBT+1wKxL37phQvEyr5zgzmQ+C7/CiXSOsYR2w= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/arch v0.0.0-20181203225421-5a4828bb7045/go.mod h1:cYlCBUl1MsqxdiKgmc4uh7TxZfWSFLOGSRR090WDxt8= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200407041343-bf15fae40dea h1:DUwLyMDMUauGMd9kSLIlhhYJNELm06HuxeBdkFkeax4= +golang.org/x/tools v0.0.0-20200407041343-bf15fae40dea/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200425043458-8463f397d07c h1:iHhCR0b26amDCiiO+kBguKZom9aMF+NrFxh9zeKR/XU= +golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools/gopls v0.4.0 h1:G4+YP9kaV4dJb79J5MobyApxX493Qa6VoiTceUmxqik= +golang.org/x/tools/gopls v0.4.0/go.mod h1:fdOZ8zb6nqlePvfek79JCskQXI4W+i2e1xT+xOPKMcY= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= @@ -128,10 +164,17 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= howett.net/plist v0.0.0-20181124034731-591f970eefbb h1:jhnBjNi9UFpfpl8YZhA9CrOqpnJdvzuiHsl/dnxl11M= howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= +mvdan.cc/xurls/v2 v2.1.0 h1:KaMb5GLhlcSX+e+qhbRJODnUUBvlw01jt4yrjFIHAuA= +mvdan.cc/xurls/v2 v2.1.0/go.mod h1:5GrSd9rOnKOpZaji1OZLYL/yeAAtGDlo/cFe+8K5n8E= +mvdan.cc/xurls/v2 v2.2.0 h1:NSZPykBXJFCetGZykLAxaL6SIpvbVy/UFEniIfHAa8A= +mvdan.cc/xurls/v2 v2.2.0/go.mod h1:EV1RMtya9D6G5DMYPGD8zTQzaHet6Jh8gFlRgGRJeO8=