summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSean E. Russell <ser@ser1.net>2020-04-27 13:53:45 -0500
committerSean E. Russell <ser@ser1.net>2020-04-27 13:53:45 -0500
commitb7d65b758775a7c9c74098602555f2d1454c2633 (patch)
tree60d3a583085a6ba2dcead5a8dddf4afbc03a35f8
parent6bad48b7a16569ea3c2d8e18a705a5f0ad90d8ec (diff)
Change from docopt to pflag to support extensions having options
-rw-r--r--cmd/gotop/main.go250
-rw-r--r--devices/devices.go20
-rw-r--r--go.mod5
-rw-r--r--go.sum43
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|layouts|colorschemes|paths|keys>
- 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)
- if err != nil {
- return err
- }
-
- if val, _ := args["--layout"]; val != nil {
- conf.Layout = val.(string)
- }
- 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)
+ 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|layouts|colorschemes|paths|keys>
+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 val, _ := args["--test"]; val != nil {
- conf.Test = val.(bool)
+ if *help {
+ flag.Usage()
+ os.Exit(0)
}
- 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
+ cs, err := colorschemes.FromName(conf.ConfigDir, *color)
+ if err != nil {
+ return err
}
- if args["--mbps"].(bool) {
- conf.Mbps = true
+ conf.Colorscheme = cs
+ if *fahrenheit {
+ conf.TempScale = 'F'
+ } else {
+ conf.TempScale = 'C'
}
- 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 <C-c>
-Process navigation:
- k and <Up>: up
- j and <Down>: down
- <C-u>: half page up
- <C-d>: half page down
- <C-b>: full page up
- <C-f>: full page down
- gg and <Home>: jump to top
- G and <End>: jump to bottom
-Process actions:
- <Tab>: 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):
- <Enter> accept filter
- <C-c> and <Escape>: 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 <C-c>
+Process navigation:
+ k and <Up>: up
+ j and <Down>: down
+ <C-u>: half page up
+ <C-d>: half page down
+ <C-b>: full page up
+ <C-f>: full page down
+ gg and <Home>: jump to top
+ G and <End>: jump to bottom
+Process actions:
+ <Tab>: 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):
+ <Enter> accept filter
+ <C-c> and <Escape>: 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=