From 09ee1c626219a527339bb16ff0694d353af8e58e Mon Sep 17 00:00:00 2001 From: Zellyn Hunter Date: Sat, 31 Jul 2021 22:10:44 -0400 Subject: [PATCH] major refactor Major refactor to use kong instead of cobra. All functionality that worked before should still be working now. Added `reorder` command --- README.md | 34 +- cmd/applesoft.go | 76 +--- cmd/catalog.go | 6 +- cmd/delete.go | 67 ++-- cmd/dump.go | 51 ++- cmd/nakedos.go | 122 +++--- cmd/put.go | 80 ++-- cmd/reorder.go | 48 +-- cmd/root.go | 31 -- cmd/sd.go | 90 ++--- data/disks/prodos-users-disk.dsk | Bin 0 -> 143360 bytes disk/disk.go | 21 +- disk/open.go | 138 ++++++- dos3/dos3.go | 31 +- generate.go | 8 - go.mod | 5 +- go.sum | 653 +------------------------------ helpers/helpers.go | 17 +- main.go | 44 ++- make-data.sh | 6 - next | 6 + prodos/prodos.go | 112 ++++-- script_test.go | 25 ++ supermon/supermon.go | 52 ++- supermon/supermon_test.go | 4 +- testdata/cathello.txt | 7 + types/ops.go | 16 + 27 files changed, 603 insertions(+), 1147 deletions(-) delete mode 100644 cmd/root.go create mode 100644 data/disks/prodos-users-disk.dsk delete mode 100644 generate.go delete mode 100755 make-data.sh create mode 100755 next create mode 100644 script_test.go create mode 100644 testdata/cathello.txt diff --git a/README.md b/README.md index 214584f..c79cf24 100644 --- a/README.md +++ b/README.md @@ -28,9 +28,32 @@ diskii's major disadvantage is that it mostly doesn't exist yet. It rhymes with “whiskey”. -Discussion/support is in -[#apple2 on the retrocomputing Slack](https://retrocomputing.slack.com/messages/apple2/) -(invites [here](https://retrocomputing.herokuapp.com)). +Discussion/support is on the +[apple2infinitum Slack](https://apple2infinitum.slack.com/) +(invites [here](http://apple2.gs:3000/)). + +# Examples + +Get a listing of files on a DOS 3.3 disk image: +``` +diskii ls dos33master.dsk +``` + +… or a ProDOS disk image: +``` +diskii ls ProDOS_2_4_2.po +``` + +… or a Super-Mon disk image: +``` +diskii ls Super-Mon-2.0.dsk +``` + +Reorder the sectors in a disk image: +``` +diskii reorder ProDOS_2_4_2.dsk ProDOS_2_4_2.po +``` + # Goals @@ -74,14 +97,15 @@ My rough TODO list (apart from anything marked (✗) in the disk operations matrix is listed below. Anything that an actual user needs will be likely to get priority. +- [ ] Make `put` accept load address for appropriate filetypes. +- [ ] Implement `GetFile` for prodos - [x] Build per-platform binaries for Linux, MacOS, Windows. - [x] Implement `GetFile` for DOS 3.3 - [ ] Add and implement the `-l` flag for `ls` - [x] Add `Delete` to the `disk.Operator` interface - [ ] Implement it for Super-Mon - [ ] Implement it for DOS 3.3 -- [x] Add basic ProDOS structures -- [ ] Add ProDOS support +- [ ] Add ProDOS support for all commands - [x] Make `filetypes` command use a tabwriter to write as a table # Related tools diff --git a/cmd/applesoft.go b/cmd/applesoft.go index 2907742..5cfb25c 100644 --- a/cmd/applesoft.go +++ b/cmd/applesoft.go @@ -3,87 +3,41 @@ package cmd import ( - "fmt" "os" - "github.com/spf13/cobra" "github.com/zellyn/diskii/basic" "github.com/zellyn/diskii/basic/applesoft" "github.com/zellyn/diskii/helpers" + "github.com/zellyn/diskii/types" ) -// applesoftCmd represents the applesoft command -var applesoftCmd = &cobra.Command{ - Use: "applesoft", - Short: "work with applesoft programs", - Long: `diskii applesoft contains the subcommands useful for working - with Applesoft programs.`, +type ApplesoftCmd struct { + Decode DecodeCmd `kong:"cmd,help='Convert a binary Applesoft program to a text LISTing.'"` } -func init() { - RootCmd.AddCommand(applesoftCmd) +type DecodeCmd struct { + Filename string `kong:"arg,default='-',type='existingfile',help='Binary Applesoft file to read, or “-” for stdin.'"` - // Here you will define your flags and configuration settings. - - // Cobra supports Persistent Flags which will work for this command - // and all subcommands, e.g.: - // applesoftCmd.PersistentFlags().String("foo", "", "A help for foo") - - // Cobra supports local flags which will only run when this command - // is called directly, e.g.: - // applesoftCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + Location uint16 `kong:"type='anybaseuint16',default='0x801',help='Starting program location in memory.'"` + Raw bool `kong:"short='r',help='Print raw control codes (no escaping)'"` } -// ----- applesoft decode command ------------------------------------------- - -var location uint16 // flag for starting location in memory -var rawControlCodes bool // flag for whether to skip escaping control codes - -// decodeCmd represents the decode command -var decodeCmd = &cobra.Command{ - Use: "decode filename", - Short: "convert a binary applesoft program to a LISTing", - Long: ` -decode converts a binary Applesoft program to a text LISTing. - -Examples: -decode filename # read filename -decode - # read stdin`, - Run: func(cmd *cobra.Command, args []string) { - if err := runDecode(args); err != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(-1) - } - }, +func (d DecodeCmd) Help() string { + return `Examples: + # Dump the contents of HELLO and then decode it. + diskii dump dos33master.dsk HELLO | diskii applesoft decode -` } -func init() { - applesoftCmd.AddCommand(decodeCmd) - - // Here you will define your flags and configuration settings. - - // Cobra supports Persistent Flags which will work for this command - // and all subcommands, e.g.: - // decodeCmd.PersistentFlags().String("foo", "", "A help for foo") - - decodeCmd.Flags().Uint16VarP(&location, "location", "l", 0x801, "Starting program location in memory") - decodeCmd.Flags().BoolVarP(&rawControlCodes, "raw", "r", false, "Print raw control codes (no escaping)") -} - -// runDecode performs the actual decode logic. -func runDecode(args []string) error { - if len(args) != 1 { - return fmt.Errorf("decode expects one argument: the filename (or - for stdin)") - } - contents, err := helpers.FileContentsOrStdIn(args[0]) +func (d *DecodeCmd) Run(globals *types.Globals) error { + contents, err := helpers.FileContentsOrStdIn(d.Filename) if err != nil { return err } - listing, err := applesoft.Decode(contents, location) + listing, err := applesoft.Decode(contents, d.Location) if err != nil { return err } - if rawControlCodes { + if d.Raw { os.Stdout.WriteString(listing.String()) } else { os.Stdout.WriteString(basic.ChevronControlCodes(listing.String())) diff --git a/cmd/catalog.go b/cmd/catalog.go index ba4c9b1..3156d16 100644 --- a/cmd/catalog.go +++ b/cmd/catalog.go @@ -11,8 +11,8 @@ import ( ) type LsCmd struct { - Order string `kong:"default='auto',enum='auto,do,po',help='Logical-to-physical sector order.'"` - System string `kong:"default='auto',enum='auto,dos3',help='DOS system used for image.'"` + Order types.DiskOrder `kong:"default='auto',enum='auto,do,po',help='Logical-to-physical sector order.'"` + System string `kong:"default='auto',enum='auto,dos3',help='DOS system used for image.'"` ShortNames bool `kong:"short='s',help='Whether to print short filenames (only makes a difference on Super-Mon disks).'"` Image *os.File `kong:"arg,required,help='Disk/device image to read.'"` @@ -20,7 +20,7 @@ type LsCmd struct { } func (l *LsCmd) Run(globals *types.Globals) error { - op, order, err := disk.OpenImage(l.Image, l.Order, l.System, globals) + op, order, err := disk.OpenFile(l.Image, l.Order, l.System, globals.DiskOperatorFactories, globals.Debug) if err != nil { return fmt.Errorf("%w: %s", err, l.Image.Name()) } diff --git a/cmd/delete.go b/cmd/delete.go index 8d2a08e..3ad470e 100644 --- a/cmd/delete.go +++ b/cmd/delete.go @@ -2,57 +2,40 @@ package cmd -/* -var missingok bool // flag for whether to consider deleting a nonexistent file okay - -// deleteCmd represents the delete command, used to delete a file. -var deleteCmd = &cobra.Command{ - Use: "delete", - Short: "delete a file", - Long: `Delete a file. - -delete disk-image.dsk HELLO -`, - Run: func(cmd *cobra.Command, args []string) { - if err := runDelete(args); err != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(-1) - } - }, +import ( + "fmt" + + "github.com/zellyn/diskii/disk" + "github.com/zellyn/diskii/types" +) + +type DeleteCmd struct { + Order types.DiskOrder `kong:"default='auto',enum='auto,do,po',help='Logical-to-physical sector order.'"` + System string `kong:"default='auto',enum='auto,dos3',help='DOS system used for image.'"` + MissingOk bool `kong:"short='f',help='Overwrite existing file?'"` + + DiskImage string `kong:"arg,required,type='existingfile',help='Disk image to modify.'"` + Filename string `kong:"arg,required,help='Filename to use on disk.'"` } -func init() { - RootCmd.AddCommand(deleteCmd) - deleteCmd.Flags().BoolVarP(&missingok, "missingok", "f", false, "if true, don't consider deleting a nonexistent file an error") +func (d DeleteCmd) Help() string { + return `Examples: + # Delete file GREMLINS on disk image games.dsk. + diskii rm games.dsk GREMLINS` } -// runDelete performs the actual delete logic. -func runDelete(args []string) error { - if len(args) != 2 { - return fmt.Errorf("delete expects a disk image filename, and a filename") - } - op, err := disk.Open(args[0]) - if err != nil { - return err - } - deleted, err := op.Delete(args[1]) +func (d *DeleteCmd) Run(globals *types.Globals) error { + op, order, err := disk.OpenFilename(d.DiskImage, d.Order, d.System, globals.DiskOperatorFactories, globals.Debug) if err != nil { return err } - if !deleted && !missingok { - return fmt.Errorf("file %q not found", args[1]) - } - f, err := os.Create(args[0]) - if err != nil { - return err - } - _, err = op.Write(f) + + deleted, err := op.Delete(d.Filename) if err != nil { return err } - if err = f.Close(); err != nil { - return err + if !deleted && !d.MissingOk { + return fmt.Errorf("file %q not found (use -f to prevent this being an error)", d.Filename) } - return nil + return disk.WriteBack(d.DiskImage, op, order, true) } -*/ diff --git a/cmd/dump.go b/cmd/dump.go index 6f81db6..d53884e 100644 --- a/cmd/dump.go +++ b/cmd/dump.go @@ -2,43 +2,40 @@ package cmd -/* -// dumpCmd represents the dump command, used to dump the raw contents -// of a file. -var dumpCmd = &cobra.Command{ - Use: "dump", - Short: "dump the raw contents of a file", - Long: `Dump the raw contents of a file. - -dump disk-image.dsk HELLO -`, - Run: func(cmd *cobra.Command, args []string) { - if err := runDump(args); err != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(-1) - } - }, +import ( + "os" + + "github.com/zellyn/diskii/disk" + "github.com/zellyn/diskii/types" +) + +type DumpCmd struct { + Order types.DiskOrder `kong:"default='auto',enum='auto,do,po',help='Logical-to-physical sector order.'"` + System string `kong:"default='auto',enum='auto,dos3',help='DOS system used for image.'"` + + DiskImage string `kong:"arg,required,type='existingfile',help='Disk image to modify.'"` + Filename string `kong:"arg,required,help='Filename to use on disk.'"` } -func init() { - RootCmd.AddCommand(dumpCmd) +func (d DumpCmd) Help() string { + return `Examples: + # Dump file GREMLINS on disk image games.dsk. + diskii dump games.dsk GREMLINS` } -// runDump performs the actual dump logic. -func runDump(args []string) error { - if len(args) != 2 { - return fmt.Errorf("dump expects a disk image filename, and a filename") +func (d *DumpCmd) Run(globals *types.Globals) error { + op, _, err := disk.OpenFilename(d.DiskImage, d.Order, d.System, globals.DiskOperatorFactories, globals.Debug) + if err != nil { + return err } - op, err := disk.Open(args[0]) + + file, err := op.GetFile(d.Filename) if err != nil { return err } - file, err := op.GetFile(args[1]) + _, err = os.Stdout.Write(file.Data) if err != nil { return err } - // TODO(zellyn): allow writing to files - os.Stdout.Write(file.Data) return nil } -*/ diff --git a/cmd/nakedos.go b/cmd/nakedos.go index 8800aad..bf6e8a6 100644 --- a/cmd/nakedos.go +++ b/cmd/nakedos.go @@ -2,81 +2,78 @@ package cmd -/* -// nakedosCmd represents the nakedos command -var nakedosCmd = &cobra.Command{ - Use: "nakedos", - Short: "work with NakedOS disks", - Long: `diskii nakedos contains the subcommands useful for working -with NakedOS (and Super-Mon) disks`, - Aliases: []string{"supermon"}, -} - -func init() { - RootCmd.AddCommand(nakedosCmd) -} +import ( + "fmt" -// ----- mkhello command ---------------------------------------------------- + "github.com/zellyn/diskii/disk" + "github.com/zellyn/diskii/supermon" + "github.com/zellyn/diskii/types" +) -var address uint16 // flag for address to load at -var start uint16 // flag for address to start execution at const helloName = "FHELLO" // filename to use (if Super-Mon) -// mkhelloCmd represents the mkhello command -var mkhelloCmd = &cobra.Command{ - Use: "mkhello filename", - Short: "create an FHELLO program that loads and runs another file", - Long: ` -mkhello creates file DF01:FHELLO that loads and runs another program at a specific address. +type NakedOSCmd struct { + Mkhello MkHelloCmd `kong:"cmd,help='Create an FHELLO program that loads and runs another file.'"` +} -Examples: -mkhello test.dsk FDEMO # load and run FDEMO at the default address, then jump to the start of the loaded code. -mkhello test.dsk --address 0x2000 --start 0x2100 DF06 # load and run file DF06 at address 0x2000, and jump to 0x2100.`, - Run: func(cmd *cobra.Command, args []string) { - if err := runMkhello(args); err != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(-1) - } - }, +// Help shows extended help on NakedOS/Super-Mon. +func (n NakedOSCmd) Help() string { + return `NakedOS and Super-Mon were created by the amazing Martin Haye. For more information see: + Source/docs: https://bitbucket.org/martin.haye/super-mon/ + Presentation: https://www.kansasfest.org/2012/08/2010-haye-nakedos/` } -func init() { - nakedosCmd.AddCommand(mkhelloCmd) +type MkHelloCmd struct { + Order types.DiskOrder `kong:"default='auto',enum='auto,do,po',help='Logical-to-physical sector order.'"` - // Here you will define your flags and configuration settings. + DiskImage string `kong:"arg,required,type='existingfile',help='Disk image to modify.'"` + Filename string `kong:"arg,required,help='Name of NakedOS file to load.'"` - mkhelloCmd.Flags().Uint16VarP(&address, "address", "a", 0x6000, "memory location to load code at") - mkhelloCmd.Flags().Uint16VarP(&start, "start", "s", 0x6000, "memory location to jump to") + Address uint16 `kong:"type='anybaseuint16',default='0x6000',help='Address to load the code at.'"` + Start uint16 `kong:"type='anybaseuint16',default='0xFFFF',help='Address to jump to. Defaults to 0xFFFF, which means “same as address flag”'"` } -// runMkhello performs the actual mkhello logic. -func runMkhello(args []string) error { - if len(args) != 2 { - return fmt.Errorf("usage: diskii mkhello ") +func (m MkHelloCmd) Help() string { + return `This command creates a very short DF01:FHELLO program that simply loads another program of your choice. + +Examples: + # Load and run FDEMO at the default address, then jump to the start of the loaded code. + mkhello test.dsk FDEMO + + # Load and run file DF06 at address 0x2000, and jump to 0x2100. + mkhello test.dsk --address 0x2000 --start 0x2100 DF06` +} + +func (m *MkHelloCmd) Run(globals *types.Globals) error { + if m.Start == 0xFFFF { + m.Start = m.Address } - if address%256 != 0 { - return fmt.Errorf("address %d (%04X) not on a page boundary", address, address) + + if m.Address%256 != 0 { + return fmt.Errorf("address %d (%04X) not on a page boundary", m.Address, m.Address) } - if start < address { - return fmt.Errorf("start address %d (%04X) < load address %d (%04X)", start, start, address, address) + if m.Start < m.Address { + return fmt.Errorf("start address %d (%04X) < load address %d (%04X)", m.Start, m.Start, m.Address, m.Address) } - op, err := disk.Open(args[0]) + + op, order, err := disk.OpenFilename(m.DiskImage, m.Order, "auto", globals.DiskOperatorFactories, globals.Debug) if err != nil { return err } + if op.Name() != "nakedos" { return fmt.Errorf("mkhello only works on disks of type %q; got %q", "nakedos", op.Name()) } nakOp, ok := op.(supermon.Operator) if !ok { - return fmt.Errorf("internal error: cannot cast to expected supermon.Operator type") + return fmt.Errorf("internal error: cannot cast to expected supermon.Operator type (got %T)", op) } - addr, symbolAddr, _, err := nakOp.ST.FilesForCompoundName(args[1]) + addr, symbolAddr, _, err := nakOp.ST.FilesForCompoundName(m.Filename) if err != nil { return err } if addr == 0 && symbolAddr == 0 { - return fmt.Errorf("cannot parse %q as valid filename", args[1]) + return fmt.Errorf("cannot parse %q as valid filename", m.Filename) } toLoad := addr if addr == 0 { @@ -86,33 +83,24 @@ func runMkhello(args []string) error { 0x20, 0x40, 0x03, // JSR NAKEDOS 0x6D, 0x01, 0xDC, // ADC NKRDFILE 0x2C, toLoad, 0xDF, // BIT ${file number to load} - 0x2C, 0x00, byte(address >> 8), // BIT ${target page} - 0xD8, // CLD - 0x4C, byte(start), byte(start >> 8), // JMP ${target page} + 0x2C, 0x00, byte(m.Address >> 8), // BIT ${target page} + 0xD8, // CLD + 0x4C, byte(m.Start), byte(m.Start >> 8), // JMP ${target page} } - fileInfo := disk.FileInfo{ - Descriptor: disk.Descriptor{ + fileInfo := types.FileInfo{ + Descriptor: types.Descriptor{ Name: fmt.Sprintf("DF01:%s", helloName), Length: len(contents), - Type: disk.FiletypeBinary, + Type: types.FiletypeBinary, }, Data: contents, } + _ = fileInfo + _, err = op.PutFile(fileInfo, true) if err != nil { return err } - f, err := os.Create(args[0]) - if err != nil { - return err - } - _, err = op.Write(f) - if err != nil { - return err - } - if err = f.Close(); err != nil { - return err - } - return nil + + return disk.WriteBack(m.DiskImage, op, order, true) } -*/ diff --git a/cmd/put.go b/cmd/put.go index 50b71d0..9143911 100644 --- a/cmd/put.go +++ b/cmd/put.go @@ -2,75 +2,63 @@ package cmd -/* -var filetypeName string // flag for file type -var overwrite bool // flag for whether to overwrite +import ( + "fmt" -// putCmd represents the put command, used to put the raw contents -// of a file. -var putCmd = &cobra.Command{ - Use: "put", - Short: "put the raw contents of a file", - Long: `Put the raw contents of a file. + "github.com/zellyn/diskii/disk" + "github.com/zellyn/diskii/helpers" + "github.com/zellyn/diskii/types" +) -put disk-image.dsk HELLO -`, - Run: func(cmd *cobra.Command, args []string) { - if err := runPut(args); err != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(-1) - } - }, +type PutCmd struct { + Order types.DiskOrder `kong:"default='auto',enum='auto,do,po',help='Logical-to-physical sector order.'"` + System string `kong:"default='auto',enum='auto,dos3',help='DOS system used for image.'"` + FiletypeName string `kong:"default='B',help='Type of file (“diskii filetypes” to list).'"` + Overwrite bool `kong:"short='f',help='Overwrite existing file?'"` + + DiskImage string `kong:"arg,required,type='existingfile',help='Disk image to modify.'"` + TargetFilename string `kong:"arg,required,help='Filename to use on disk.'"` + SourceFilename string `kong:"arg,required,type='existingfile',help='Name of file containing data to put.'"` } -func init() { - RootCmd.AddCommand(putCmd) - putCmd.Flags().StringVarP(&filetypeName, "type", "t", "B", "Type of file (`diskii filetypes` to list)") - putCmd.Flags().BoolVarP(&overwrite, "overwrite", "f", false, "whether to overwrite existing files") +func (p PutCmd) Help() string { + return `Examples: + # Put file gremlins.o onto disk image games.dsk, using the filename GREMLINS. + diskii put games.dsk GREMLINS gremlins.o` } -// runPut performs the actual put logic. -func runPut(args []string) error { - if len(args) != 3 { - return fmt.Errorf("usage: put ") +func (p *PutCmd) Run(globals *types.Globals) error { + if p.DiskImage == "-" { + if p.SourceFilename == "-" { + return fmt.Errorf("cannot get both disk image and file contents from stdin") + } } - op, err := disk.Open(args[0]) + op, order, err := disk.OpenFilename(p.DiskImage, p.Order, p.System, globals.DiskOperatorFactories, globals.Debug) if err != nil { return err } - contents, err := helpers.FileContentsOrStdIn(args[2]) + + contents, err := helpers.FileContentsOrStdIn(p.SourceFilename) if err != nil { return err } - filetype, err := disk.FiletypeForName(filetypeName) + filetype, err := types.FiletypeForName(p.FiletypeName) if err != nil { return err } - - fileInfo := disk.FileInfo{ - Descriptor: disk.Descriptor{ - Name: args[1], + fileInfo := types.FileInfo{ + Descriptor: types.Descriptor{ + Name: p.TargetFilename, Length: len(contents), Type: filetype, }, Data: contents, } - _, err = op.PutFile(fileInfo, overwrite) - if err != nil { - return err - } - f, err := os.Create(args[0]) - if err != nil { - return err - } - _, err = op.Write(f) + _, err = op.PutFile(fileInfo, p.Overwrite) if err != nil { return err } - if err = f.Close(); err != nil { - return err - } - return nil + + return disk.WriteBack(p.DiskImage, op, order, true) } -*/ diff --git a/cmd/reorder.go b/cmd/reorder.go index 9cc04b2..98c00f3 100644 --- a/cmd/reorder.go +++ b/cmd/reorder.go @@ -2,8 +2,6 @@ package cmd import ( "fmt" - "path" - "strings" "github.com/zellyn/diskii/disk" "github.com/zellyn/diskii/helpers" @@ -11,9 +9,9 @@ import ( ) type ReorderCmd struct { - Order string `kong:"default='auto',enum='auto,do,po',help='Logical-to-physical sector order.'"` - NewOrder string `kong:"default='auto',enum='auto,do,po',help='New Logical-to-physical sector order.'"` - Force bool `kong:"short='s',help='Overwrite existing file?'"` + Order types.DiskOrder `kong:"default='auto',enum='auto,do,po',help='Logical-to-physical sector order.'"` + NewOrder types.DiskOrder `kong:"default='auto',enum='auto,do,po',help='New Logical-to-physical sector order.'"` + Overwrite bool `kong:"short='f',help='Overwrite existing file?'"` DiskImage string `kong:"arg,required,type='existingfile',help='Disk image to read.'"` NewDiskImage string `kong:"arg,optional,type='path',help='Disk image to write, if different.'"` @@ -44,11 +42,18 @@ func (r *ReorderCmd) Run(globals *types.Globals) error { if err != nil { return err } - return helpers.WriteOutput(r.NewDiskImage, tobytes, r.DiskImage, r.Force) + + overwrite := r.Overwrite + filename := r.NewDiskImage + if filename == "" { + filename = r.DiskImage + overwrite = true + } + return helpers.WriteOutput(filename, tobytes, overwrite) } // getOrders returns the input order, and the output order. -func getOrders(inFilename string, inOrder string, outFilename string, outOrder string) (string, string, error) { +func getOrders(inFilename string, inOrder types.DiskOrder, outFilename string, outOrder types.DiskOrder) (types.DiskOrder, types.DiskOrder, error) { if inOrder == "auto" && outOrder != "auto" { return oppositeOrder(outOrder), outOrder, nil } @@ -62,40 +67,27 @@ func getOrders(inFilename string, inOrder string, outFilename string, outOrder s return "", "", fmt.Errorf("identical order and new-order") } - inGuess, outGuess := orderFromFilename(inFilename), orderFromFilename(outFilename) + inGuess, outGuess := disk.OrderFromFilename(inFilename, types.DiskOrderUnknown), disk.OrderFromFilename(outFilename, types.DiskOrderUnknown) if inGuess == outGuess { - if inGuess == "" { + if inGuess == types.DiskOrderUnknown { return "", "", fmt.Errorf("cannot determine input or output order from file extensions") } return "", "", fmt.Errorf("guessed order (%s) from file %q is the same as guessed order (%s) from file %q", inGuess, inFilename, outGuess, outFilename) } - if inGuess == "" { + if inGuess == types.DiskOrderUnknown { return oppositeOrder(outGuess), outGuess, nil } - if outGuess == "" { + if outGuess == types.DiskOrderUnknown { return inGuess, oppositeOrder(inGuess), nil } return inGuess, outGuess, nil } // oppositeOrder returns the opposite order from the input. -func oppositeOrder(order string) string { - if order == "do" { - return "po" - } - return "do" -} - -// orderFromFilename tries to guess the disk order from the filename, using the extension. -func orderFromFilename(filename string) string { - ext := strings.ToLower(path.Ext(filename)) - switch ext { - case ".dsk", ".do": - return "do" - case ".po": - return "po" - default: - return "" +func oppositeOrder(order types.DiskOrder) types.DiskOrder { + if order == types.DiskOrderDO { + return types.DiskOrderPO } + return types.DiskOrderDO } diff --git a/cmd/root.go b/cmd/root.go deleted file mode 100644 index 97b565d..0000000 --- a/cmd/root.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright © 2016 Zellyn Hunter - -package cmd - -import ( - "fmt" - "os" - - "github.com/spf13/cobra" -) - -var cfgFile string - -// RootCmd represents the base command when called without any subcommands -var RootCmd = &cobra.Command{ - Use: "diskii", - Short: "Operate on Apple II disk images and their contents", - Long: `diskii is a commandline tool for working with Apple II disk -images. - -Eventually, it aims to be a comprehensive disk image manipulation tool.`, -} - -// Execute adds all child commands to the root command sets flags appropriately. -// This is called by main.main(). It only needs to happen once to the rootCmd. -func Execute() { - if err := RootCmd.Execute(); err != nil { - fmt.Println(err) - os.Exit(-1) - } -} diff --git a/cmd/sd.go b/cmd/sd.go index 61a995f..126890d 100644 --- a/cmd/sd.go +++ b/cmd/sd.go @@ -4,68 +4,62 @@ package cmd import ( "fmt" + "io" "os" - "github.com/spf13/cobra" "github.com/zellyn/diskii/disk" "github.com/zellyn/diskii/helpers" + "github.com/zellyn/diskii/types" ) -var sdAddress uint16 // flag for address to load at -var sdStart uint16 // flag for address to start execution at +type SDCmd struct { + Order types.DiskOrder `kong:"default='auto',enum='auto,do,po',help='Logical-to-physical sector order.'"` -// mksdCmd represents the mksd command -var mksdCmd = &cobra.Command{ - Use: "mksd", - Short: "create a Standard-Delivery disk image", - Long: `diskii mksd creates a "Standard Delivery" disk image containing a binary. + DiskImage string `kong:"arg,required,type='path',help='Disk image to write.'"` + Binary *os.File `kong:"arg,required,help='Binary file to write to the disk.'"` + + Address uint16 `kong:"type='anybaseuint16',default='0x6000',help='Address to load the code at.'"` + Start uint16 `kong:"type='anybaseuint16',default='0xFFFF',help='Address to jump to. Defaults to 0xFFFF, which means “same as address flag”'"` +} + +func (s SDCmd) Help() string { + return ` See https://github.com/peterferrie/standard-delivery for details. Examples: -mksd test.dsk foo.o # load and run foo.o at the default address, then jump to the start of the loaded code. -mksd test.dsk foo.o --address 0x2000 --start 0x2100 # load foo.o at address 0x2000, then jump to 0x2100.`, - Run: func(cmd *cobra.Command, args []string) { - if err := runMkSd(args); err != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(-1) - } - }, -} + # Load and run foo.o at the default address, then jump to the start of the loaded code. + diskii mksd test.dsk foo.o -func init() { - RootCmd.AddCommand(mksdCmd) - mksdCmd.Flags().Uint16VarP(&sdAddress, "address", "a", 0x6000, "memory location to load code at") - mksdCmd.Flags().Uint16VarP(&sdStart, "start", "s", 0x6000, "memory location to jump to") + # Load foo.o at address 0x2000, then jump to 0x2100. + diskii mksd test.dsk foo.o --address 0x2000 --start 0x2100` } -// ----- mksd command ------------------------------------------------------- - -// runMkSd performs the actual mksd logic. -func runMkSd(args []string) error { - if len(args) != 2 { - return fmt.Errorf("usage: diskii mksd ") +func (s *SDCmd) Run(globals *types.Globals) error { + if s.Start == 0xFFFF { + s.Start = s.Address } - contents, err := helpers.FileContentsOrStdIn(args[1]) + + contents, err := io.ReadAll(s.Binary) if err != nil { return err } - if sdAddress%256 != 0 { - return fmt.Errorf("address %d (%04X) not on a page boundary", sdAddress, sdAddress) + if s.Address%256 != 0 { + return fmt.Errorf("address %d (%04X) not on a page boundary", s.Address, s.Address) } - if sdStart < sdAddress { - return fmt.Errorf("start address %d (%04X) < load address %d (%04X)", sdStart, sdStart, sdAddress, sdAddress) + if s.Start < s.Address { + return fmt.Errorf("start address %d (%04X) < load address %d (%04X)", s.Start, s.Start, s.Address, s.Address) } - if int(sdStart) >= int(sdAddress)+len(contents) { - end := int(sdAddress) + len(contents) + if int(s.Start) >= int(s.Address)+len(contents) { + end := int(s.Address) + len(contents) return fmt.Errorf("start address %d (%04X) is beyond load address %d (%04X) + file length = %d (%04X)", - sdStart, sdStart, sdAddress, sdAddress, end, end) + s.Start, s.Start, s.Address, s.Address, end, end) } - if int(sdStart)+len(contents) > 0xC000 { - end := int(sdStart) + len(contents) + if int(s.Start)+len(contents) > 0xC000 { + end := int(s.Start) + len(contents) return fmt.Errorf("start address %d (%04X) + file length %d (%04X) = %d (%04X), but we can't load past page 0xBF00", - sdStart, sdStart, len(contents), len(contents), end, end) + s.Start, s.Start, len(contents), len(contents), end, end) } sectors := (len(contents) + 255) / 256 @@ -76,11 +70,11 @@ func runMkSd(args []string) error { 0xc8, 0xa5, 0x27, 0xf0, 0xdf, 0x8a, 0x4a, 0x4a, 0x4a, 0x4a, 0x09, 0xc0, 0x48, 0xa9, 0x5b, 0x48, 0x60, 0xe6, 0x41, 0x06, 0x40, 0x20, 0x37, 0x08, 0x18, 0x20, 0x3c, 0x08, 0xe6, 0x40, 0xa5, 0x40, 0x29, 0x03, 0x2a, 0x05, 0x2b, 0xa8, 0xb9, 0x80, 0xc0, 0xa9, 0x30, 0x4c, 0xa8, - 0xfc, 0x4c, byte(sdStart), byte(sdStart >> 8), + 0xfc, 0x4c, byte(s.Start), byte(s.Start >> 8), } if len(loader)+sectors+1 > 256 { - return fmt.Errorf("file %q is %d bytes long, max is %d", args[1], len(contents), (255-len(loader))*256) + return fmt.Errorf("file %q is %d bytes long, max is %d", s.Binary.Name(), len(contents), (255-len(loader))*256) } for len(contents)%256 != 0 { @@ -102,7 +96,7 @@ func runMkSd(args []string) error { } } - address := int(sdAddress) + i + address := int(s.Address) + i loader = append(loader, byte(address>>8)) if err := disk.WriteSector(diskbytes, track, sector, contents[i:i+256]); err != nil { return err @@ -118,17 +112,13 @@ func runMkSd(args []string) error { return err } - f, err := os.Create(args[0]) - if err != nil { - return err + order := s.Order + if order == types.DiskOrderAuto { + order = disk.OrderFromFilename(s.DiskImage, types.DiskOrderDO) } - return fmt.Errorf("write not implemented") - //_, err = sd.Write(f) + rawBytes, err := disk.Swizzle(diskbytes, disk.PhysicalToLogicalByName[order]) if err != nil { return err } - if err = f.Close(); err != nil { - return err - } - return nil + return helpers.WriteOutput(s.DiskImage, rawBytes, true) } diff --git a/data/disks/prodos-users-disk.dsk b/data/disks/prodos-users-disk.dsk new file mode 100644 index 0000000000000000000000000000000000000000..e063af6ef39e6e60689fb70118e5e78ff31b96a8 GIT binary patch literal 143360 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 literal 0 HcmV?d00001 diff --git a/disk/disk.go b/disk/disk.go index fde6565..2b5f338 100644 --- a/disk/disk.go +++ b/disk/disk.go @@ -4,13 +4,16 @@ // file formats. package disk +import "github.com/zellyn/diskii/types" + // Various DOS33 disk characteristics. const ( FloppyTracks = 35 FloppySectors = 16 // Sectors per track // FloppyDiskBytes is the number of bytes on a DOS 3.3 disk. - FloppyDiskBytes = 143360 // 35 tracks * 16 sectors * 256 bytes - FloppyTrackBytes = 256 * FloppySectors // Bytes per track + FloppyDiskBytes = 143360 // 35 tracks * 16 sectors * 256 bytes + FloppyTrackBytes = 256 * FloppySectors // Bytes per track + FloppyDiskBytes13Sector = 35 * 13 * 256 ) // Dos33LogicalToPhysicalSectorMap maps logical sector numbers to physical ones. @@ -43,16 +46,18 @@ var ProDosPhysicalToLogicalSectorMap = []int{ // LogicalToPhysicalByName maps from "do" and "po" to the corresponding // logical-to-physical ordering. -var LogicalToPhysicalByName map[string][]int = map[string][]int{ - "do": Dos33LogicalToPhysicalSectorMap, - "po": ProDOSLogicalToPhysicalSectorMap, +var LogicalToPhysicalByName map[types.DiskOrder][]int = map[types.DiskOrder][]int{ + types.DiskOrderDO: Dos33LogicalToPhysicalSectorMap, + types.DiskOrderPO: ProDOSLogicalToPhysicalSectorMap, + types.DiskOrderRaw: {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, } // PhysicalToLogicalByName maps from "do" and "po" to the corresponding // physical-to-logical ordering. -var PhysicalToLogicalByName map[string][]int = map[string][]int{ - "do": Dos33PhysicalToLogicalSectorMap, - "po": ProDosPhysicalToLogicalSectorMap, +var PhysicalToLogicalByName map[types.DiskOrder][]int = map[types.DiskOrder][]int{ + types.DiskOrderDO: Dos33PhysicalToLogicalSectorMap, + types.DiskOrderPO: ProDosPhysicalToLogicalSectorMap, + types.DiskOrderRaw: {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, } // TrackSector is a pair of track/sector bytes. diff --git a/disk/open.go b/disk/open.go index 34de3c9..4393203 100644 --- a/disk/open.go +++ b/disk/open.go @@ -7,24 +7,81 @@ import ( "path" "strings" + "github.com/zellyn/diskii/helpers" "github.com/zellyn/diskii/types" ) -// OpenImage attempts to open an image on disk, using the provided ordering and system type. -func OpenImage(file *os.File, order string, system string, globals *types.Globals) (types.Operator, string, error) { +// OpenFilename attempts to open a disk or device image, using the provided ordering and system type. +func OpenFilename(filename string, order types.DiskOrder, system string, operatorFactories []types.OperatorFactory, debug bool) (types.Operator, types.DiskOrder, error) { + if filename == "-" { + return OpenFile(os.Stdin, order, system, operatorFactories, debug) + } + file, err := os.Open(filename) + if err != nil { + return nil, "", err + } + return OpenFile(file, order, system, operatorFactories, debug) +} + +// OpenImage attempts to open a disk or device image, using the provided ordering and system type. +// OpenImage will close the file. +func OpenFile(file *os.File, order types.DiskOrder, system string, operatorFactories []types.OperatorFactory, debug bool) (types.Operator, types.DiskOrder, error) { bb, err := io.ReadAll(file) if err != nil { return nil, "", err } - if len(bb) == FloppyDiskBytes { - return openDoOrPo(bb, order, system, globals, strings.ToLower(path.Ext(file.Name()))) + if err := file.Close(); err != nil { + return nil, "", err + } + return OpenImage(bb, file.Name(), order, system, operatorFactories, debug) +} + +// OpenImage attempts to open a disk or device image, using the provided ordering and system type. +func OpenImage(filebytes []byte, filename string, order types.DiskOrder, system string, operatorFactories []types.OperatorFactory, debug bool) (types.Operator, types.DiskOrder, error) { + ext := strings.ToLower(path.Ext(filename)) + size := len(filebytes) + if size == FloppyDiskBytes { + return openDoOrPo(filebytes, order, system, ext, operatorFactories, debug) + } + if size == FloppyDiskBytes13Sector { + return nil, "", fmt.Errorf("cannot open 13-sector disk images (yet)") + } + + if ext == ".hdv" { + return openHDV(filebytes, order, system, operatorFactories, debug) + } + return nil, "", fmt.Errorf("can only open disk-sized images and .hdv files") +} + +func openHDV(rawbytes []byte, order types.DiskOrder, system string, operatorFactories []types.OperatorFactory, debug bool) (types.Operator, types.DiskOrder, error) { + size := len(rawbytes) + if size%512 > 0 { + return nil, "", fmt.Errorf("can only open .hdv files that are a multiple of 512 bytes: %d %% 512 == %d", size, size%512) + } + if size/512 > 65536 { + return nil, "", fmt.Errorf("can only open .hdv up to size 32MiB (%d); got %d", 65536*512, size) + } + if order != "auto" && order != types.DiskOrderPO { + return nil, "", fmt.Errorf("cannot open .hdv file in %q order", order) + } + if system != "auto" && system != "prodos" { + return nil, "", fmt.Errorf("cannot open .hdv file with %q system", system) + } + for _, factory := range operatorFactories { + if factory.Name() == "prodos" { + op, err := factory.Operator(rawbytes, debug) + if err != nil { + return nil, "", err + } + return op, types.DiskOrderPO, nil + } } - return nil, "", fmt.Errorf("OpenImage not implemented yet for non-disk-sized images") + return nil, "", fmt.Errorf("unable to find prodos module to open .hdv file") // Should not happen. } -func openDoOrPo(diskbytes []byte, order string, system string, globals *types.Globals, ext string) (types.Operator, string, error) { +func openDoOrPo(rawbytes []byte, order types.DiskOrder, system string, ext string, operatorFactories []types.OperatorFactory, debug bool) (types.Operator, types.DiskOrder, error) { var factories []types.OperatorFactory - for _, factory := range globals.DiskOperatorFactories { + for _, factory := range operatorFactories { if system == "auto" || system == factory.Name() { factories = append(factories, factory) } @@ -32,18 +89,18 @@ func openDoOrPo(diskbytes []byte, order string, system string, globals *types.Gl if len(factories) == 0 { return nil, "", fmt.Errorf("cannot find disk system with name %q", system) } - orders := []string{order} + orders := []types.DiskOrder{order} switch order { - case "do", "po": + case types.DiskOrderDO, types.DiskOrderPO: // nothing more - case "auto": + case types.DiskOrderAuto: switch ext { case ".po": - orders = []string{"po"} + orders = []types.DiskOrder{types.DiskOrderPO} case ".do": - orders = []string{"do"} + orders = []types.DiskOrder{types.DiskOrderDO} case ".dsk", "": - orders = []string{"do", "po"} + orders = []types.DiskOrder{types.DiskOrderDO, types.DiskOrderPO} default: return nil, "", fmt.Errorf("unknown disk image extension: %q", ext) } @@ -52,31 +109,36 @@ func openDoOrPo(diskbytes []byte, order string, system string, globals *types.Gl } for _, order := range orders { - swizzled, err := Swizzle(diskbytes, LogicalToPhysicalByName[order]) + swizzled, err := Swizzle(rawbytes, LogicalToPhysicalByName[order]) if err != nil { return nil, "", err } for _, factory := range factories { + diskbytes, err := Swizzle(swizzled, PhysicalToLogicalByName[factory.DiskOrder()]) + if err != nil { + return nil, "", err + } + if len(orders) == 1 && system != "auto" { - if globals.Debug { + if debug { fmt.Fprintf(os.Stderr, "Attempting to open with order=%s, system=%s.\n", order, factory.Name()) } - op, err := factory.Operator(swizzled, globals.Debug) + op, err := factory.Operator(diskbytes, debug) if err != nil { return nil, "", err } return op, order, nil } - if globals.Debug { + if debug { fmt.Fprintf(os.Stderr, "Testing whether order=%s, system=%s seems to match.\n", order, factory.Name()) } - if factory.SeemsToMatch(swizzled, globals.Debug) { - op, err := factory.Operator(swizzled, globals.Debug) + if factory.SeemsToMatch(diskbytes, debug) { + op, err := factory.Operator(diskbytes, debug) if err == nil { return op, order, nil } - if globals.Debug { + if debug { fmt.Fprintf(os.Stderr, "Got error opening with order=%s, system=%s: %v\n", order, factory.Name(), err) } } @@ -140,3 +202,39 @@ func validateOrder(order []int) error { } return nil } + +// OrderFromFilename tries to guess the disk order from the filename, using the extension. +func OrderFromFilename(filename string, defaultOrder types.DiskOrder) types.DiskOrder { + ext := strings.ToLower(path.Ext(filename)) + switch ext { + case ".dsk", ".do": + return types.DiskOrderDO + case ".po": + return types.DiskOrderPO + default: + return defaultOrder + } +} + +// WriteBack writes a disk image back out. +func WriteBack(filename string, op types.Operator, diskFileOrder types.DiskOrder, overwrite bool) error { + logicalBytes := op.GetBytes() + // If it's not floppy-sized, we don't swizzle at all. + if len(logicalBytes) != FloppyDiskBytes { + return helpers.WriteOutput(filename, logicalBytes, overwrite) + } + + // Go from logical sectors for the operator back to physical sectors. + physicalBytes, err := Swizzle(logicalBytes, LogicalToPhysicalByName[op.DiskOrder()]) + if err != nil { + return err + } + + // Go from physical sectors to the disk order (DO or PO) + diskBytes, err := Swizzle(physicalBytes, PhysicalToLogicalByName[diskFileOrder]) + if err != nil { + return err + } + + return helpers.WriteOutput(filename, diskBytes, overwrite) +} diff --git a/dos3/dos3.go b/dos3/dos3.go index f10fcda..33785e2 100644 --- a/dos3/dos3.go +++ b/dos3/dos3.go @@ -671,6 +671,16 @@ func (o operator) PutFile(fileInfo types.FileInfo, overwrite bool) (existed bool return false, fmt.Errorf("%s does not implement PutFile yet", operatorName) } +// DiskOrder returns the Physical-to-Logical mapping order. +func (o operator) DiskOrder() types.DiskOrder { + return types.DiskOrderDO +} + +// GetBytes returns the disk image bytes, in logical order. +func (o operator) GetBytes() []byte { + return o.data +} + // OperatorFactory is a types.OperatorFactory for DOS 3.3 disks. type OperatorFactory struct { } @@ -682,13 +692,9 @@ func (of OperatorFactory) Name() string { // SeemsToMatch returns true if the []byte disk image seems to match the // system of this operator. -func (of OperatorFactory) SeemsToMatch(rawbytes []byte, debug bool) bool { +func (of OperatorFactory) SeemsToMatch(diskbytes []byte, debug bool) bool { // For now, just return true if we can run Catalog successfully. - swizzled, err := of.swizzle(rawbytes) - if err != nil { - return false - } - _, _, err = ReadCatalog(swizzled, debug) + _, _, err := ReadCatalog(diskbytes, debug) if err != nil { return false } @@ -696,14 +702,11 @@ func (of OperatorFactory) SeemsToMatch(rawbytes []byte, debug bool) bool { } // Operator returns an Operator for the []byte disk image. -func (of OperatorFactory) Operator(rawbytes []byte, debug bool) (types.Operator, error) { - swizzled, err := of.swizzle(rawbytes) - if err != nil { - return nil, err - } - return operator{data: swizzled, debug: debug}, nil +func (of OperatorFactory) Operator(diskbytes []byte, debug bool) (types.Operator, error) { + return operator{data: diskbytes, debug: debug}, nil } -func (of OperatorFactory) swizzle(rawbytes []byte) ([]byte, error) { - return disk.Swizzle(rawbytes, disk.Dos33PhysicalToLogicalSectorMap) +// DiskOrder returns the Physical-to-Logical mapping order. +func (of OperatorFactory) DiskOrder() types.DiskOrder { + return operator{}.DiskOrder() } diff --git a/generate.go b/generate.go deleted file mode 100644 index dbfa3e1..0000000 --- a/generate.go +++ /dev/null @@ -1,8 +0,0 @@ -// This file contains the list of commands to run to re-generate -// generated files. - -// Use go-bindata to embed static assets that we need. -//go:generate go-bindata -pkg data -prefix "data/" -o data/data.go data/disks data/boot -//go:generate goimports -w data/data.go - -package main diff --git a/go.mod b/go.mod index 510ac1f..19080b9 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,7 @@ module github.com/zellyn/diskii go 1.16 require ( - github.com/alecthomas/kong v0.2.17 // indirect + github.com/alecthomas/kong v0.2.17 github.com/kr/pretty v0.2.1 - github.com/spf13/cobra v1.1.3 - github.com/spf13/viper v1.8.1 + github.com/rogpeppe/go-internal v1.8.0 ) diff --git a/go.sum b/go.sum index 6a1cd0e..e45ee95 100644 --- a/go.sum +++ b/go.sum @@ -1,673 +1,30 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/alecthomas/kong v0.2.17 h1:URDISCI96MIgcIlQyoCAlhOmrSw6pZScBNkctg8r0W0= github.com/alecthomas/kong v0.2.17/go.mod h1:ka3VZ8GZNPXv9Ov+j4YNLkI8mTuhXyr/0ktSlqIydQQ= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -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/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= -github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= 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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44= -github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/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-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -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-20190227155943-e225da77a7e6/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/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/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-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= -gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -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.3/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.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/helpers/helpers.go b/helpers/helpers.go index eed3847..7e956d0 100644 --- a/helpers/helpers.go +++ b/helpers/helpers.go @@ -1,7 +1,7 @@ // Copyright © 2016 Zellyn Hunter -// Package helpers contains various routines used to help cobra -// commands stay succinct. +// Package helpers contains helper routines for reading and writing files, +// allowing `-` to mean stdin/stdout. package helpers import ( @@ -21,18 +21,15 @@ func FileContentsOrStdIn(s string) ([]byte, error) { return os.ReadFile(s) } -func WriteOutput(outfilename string, contents []byte, infilename string, force bool) error { - if outfilename == "" { - outfilename = infilename - } - if outfilename == "-" { +func WriteOutput(filename string, contents []byte, force bool) error { + if filename == "-" { _, err := os.Stdout.Write(contents) return err } if !force { - if _, err := os.Stat(outfilename); !errors.Is(err, fs.ErrNotExist) { - return fmt.Errorf("cannot overwrite file %q without --force (-f)", outfilename) + if _, err := os.Stat(filename); !errors.Is(err, fs.ErrNotExist) { + return fmt.Errorf("cannot overwrite file %q without --force (-f)", filename) } } - return os.WriteFile(outfilename, contents, 0666) + return os.WriteFile(filename, contents, 0666) } diff --git a/main.go b/main.go index 1729a98..ddd2f7f 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,9 @@ package main import ( + "reflect" + "strconv" + "github.com/zellyn/diskii/cmd" "github.com/zellyn/diskii/dos3" "github.com/zellyn/diskii/prodos" @@ -18,21 +21,27 @@ import ( var cli struct { Debug bool `kong:"short='v',help='Enable debug mode.'"` - Ls cmd.LsCmd `cmd:"" aliases:"cat,catalog" help:"List paths."` - Reorder cmd.ReorderCmd `cmd:"" help:"Reorder disk images."` + Ls cmd.LsCmd `cmd:"" aliases:"list,cat,catalog" help:"List files/directories on a disk."` + Reorder cmd.ReorderCmd `cmd:"" help:"Convert between DOS-order and ProDOS-order disk images."` Filetypes cmd.FiletypesCmd `cmd:"" help:"Print a list of filetypes understood by diskii."` + Put cmd.PutCmd `cmd:"" help:"Put the raw contents of a file onto a disk."` + Rm cmd.DeleteCmd `cmd:"" aliases:"delete" help:"Delete a file."` + Dump cmd.DumpCmd `cmd:"" help:"Dump the raw contents of a file."` + Nakedos cmd.NakedOSCmd `cmd:"" help:"Work with NakedOS-format disks."` + Mksd cmd.SDCmd `cmd:"" help:"Create a “Standard Delivery” disk image containing a binary."` + Applesoft cmd.ApplesoftCmd `cmd:"" help:"Work with Applesoft BASIC files."` } func run() error { - ctx := kong.Parse(&cli, kong.Name("diskii"), kong.Description("A commandline tool for working with Apple II disk images."), - kong.UsageOnError(), + // kong.UsageOnError(), kong.ConfigureHelp(kong.HelpOptions{ Compact: true, Summary: true, }), + kong.NamedMapper("anybaseuint16", hexUint16Mapper{}), ) globals := &types.Globals{ @@ -48,9 +57,36 @@ func run() error { } func main() { + err := run() if err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } } + +type hexUint16Mapper struct{} + +func (h hexUint16Mapper) Decode(ctx *kong.DecodeContext, target reflect.Value) error { + t, err := ctx.Scan.PopValue("int") + if err != nil { + return err + } + var sv string + switch v := t.Value.(type) { + case string: + sv = v + + case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64: + sv = fmt.Sprintf("%v", v) + + default: + return fmt.Errorf("expected an int but got %q (%T)", t, t.Value) + } + n, err := strconv.ParseUint(sv, 0, 16) + if err != nil { + return fmt.Errorf("expected a valid %d bit uint but got %q", 16, sv) + } + target.SetUint(n) + return nil +} diff --git a/make-data.sh b/make-data.sh deleted file mode 100755 index 0406a9d..0000000 --- a/make-data.sh +++ /dev/null @@ -1,6 +0,0 @@ -go-bindata -pkg data -o data/data.go \ - data/disks/ProDOS_2_4_1.dsk \ - data/disks/dos33master.woz \ - data/boot/prodos-new-boot0.bin \ - data/boot/prodos-old-boot0.bin -goimports -w data/data.go diff --git a/next b/next new file mode 100755 index 0000000..19ad7b8 --- /dev/null +++ b/next @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +# Quick little script to add failing commands to, so I know what I'm working on next. +set -euo pipefail +set -x +# go run . nakedos mkhello supermon-audit-new.dsk DF02 +# go run . put -f supermon-audit-new.dsk DF02:FWORLD audit.o diff --git a/prodos/prodos.go b/prodos/prodos.go index 7671c26..266c189 100644 --- a/prodos/prodos.go +++ b/prodos/prodos.go @@ -7,6 +7,7 @@ package prodos import ( "encoding/binary" "fmt" + "os" "github.com/zellyn/diskii/disk" "github.com/zellyn/diskii/types" @@ -611,11 +612,12 @@ func (sh SubdirectoryHeader) Name() string { // Volume is the in-memory representation of a device's volume // information. type Volume struct { - keyBlock *VolumeDirectoryKeyBlock - blocks []*VolumeDirectoryBlock - bitmap *VolumeBitMap - subdirsByBlock map[uint16]*Subdirectory - subdirsByName map[string]*Subdirectory + keyBlock *VolumeDirectoryKeyBlock // The key block describing the entire volume + blocks []*VolumeDirectoryBlock // The blocks in the top-level volume + bitmap *VolumeBitMap // Bitmap of which blocks are free + subdirsByBlock map[uint16]*Subdirectory // A mapping of block number to subdirectory object + subdirsByName map[string]*Subdirectory // a mapping of string to subdirectory object + firstSubdirBlocks map[uint16]uint16 // A mapping of later dir/subdir blocks to the first one in the chain } // Subdirectory is the in-memory representation of a single @@ -652,16 +654,20 @@ func (v Volume) subdirDescriptors() []FileDescriptor { // readVolume reads the entire volume and subdirectories from a device // into memory. -func readVolume(devicebytes []byte, keyBlock uint16) (Volume, error) { +func readVolume(devicebytes []byte, keyBlock uint16, debug bool) (Volume, error) { v := Volume{ - keyBlock: &VolumeDirectoryKeyBlock{}, - subdirsByBlock: make(map[uint16]*Subdirectory), - subdirsByName: make(map[string]*Subdirectory), + keyBlock: &VolumeDirectoryKeyBlock{}, + subdirsByBlock: make(map[uint16]*Subdirectory), + subdirsByName: make(map[string]*Subdirectory), + firstSubdirBlocks: make(map[uint16]uint16), } if err := disk.UnmarshalBlock(devicebytes, v.keyBlock, keyBlock); err != nil { return v, fmt.Errorf("cannot read first block of volume directory (block %d): %v", keyBlock, err) } + // if debug { + // fmt.Fprintf(os.Stderr, "keyblock: %#v\n", v.keyBlock) + // } if vbm, err := readVolumeBitMap(devicebytes, v.keyBlock.Header.BitMapPointer); err != nil { return v, err @@ -669,15 +675,29 @@ func readVolume(devicebytes []byte, keyBlock uint16) (Volume, error) { v.bitmap = &vbm } + // if debug { + // fmt.Fprintf(os.Stderr, "volume bitmap: %#v\n", v.bitmap) + // } + for block := v.keyBlock.Next; block != 0; block = v.blocks[len(v.blocks)-1].Next { vdb := VolumeDirectoryBlock{} if err := disk.UnmarshalBlock(devicebytes, &vdb, block); err != nil { return v, err } v.blocks = append(v.blocks, &vdb) + v.firstSubdirBlocks[block] = keyBlock + if debug { + fmt.Fprintf(os.Stderr, " firstSubdirBlocks[%d] → %d\n", block, keyBlock) + } + // if debug { + // fmt.Fprintf(os.Stderr, "block: %#v\n", vdb) + // } } sdds := v.subdirDescriptors() + if debug { + fmt.Fprintf(os.Stderr, "got %d top-level subdir descriptors\n", len(sdds)) + } for i := 0; i < len(sdds); i++ { sdd := sdds[i] @@ -686,12 +706,27 @@ func readVolume(devicebytes []byte, keyBlock uint16) (Volume, error) { return v, err } v.subdirsByBlock[sdd.KeyPointer] = &sub + if debug { + fmt.Fprintf(os.Stderr, " subdirsByBlock[%d] → %q\n", sdd.KeyPointer, sub.keyBlock.Header.Name()) + } sdds = append(sdds, sub.subdirDescriptors()...) + for _, block := range sub.blocks { + v.firstSubdirBlocks[block.block] = sdd.KeyPointer + if debug { + fmt.Fprintf(os.Stderr, " firstSubdirBlocks[%d] → %d\n", block.block, sdd.KeyPointer) + } + } + } + if debug { + fmt.Fprintf(os.Stderr, "got %d total subdir descriptors\n", len(sdds)) } for _, sd := range v.subdirsByBlock { name := sd.keyBlock.Header.Name() - parentName, err := parentDirName(sd.keyBlock.Header.ParentPointer, keyBlock, v.subdirsByBlock) + if debug { + fmt.Fprintf(os.Stderr, "processing subdir %q\n", name) + } + parentName, err := parentDirName(sd.keyBlock.Header.ParentPointer, keyBlock, v.subdirsByBlock, v.firstSubdirBlocks) if err != nil { return v, err } @@ -701,6 +736,9 @@ func readVolume(devicebytes []byte, keyBlock uint16) (Volume, error) { v.subdirsByName[name] = sd } + if debug { + fmt.Fprintf(os.Stderr, "HERE2\n") + } return v, nil } @@ -729,16 +767,23 @@ func (s Subdirectory) subdirDescriptors() []FileDescriptor { return descs } -// fullDirName returns the full recursive directory name of the given parent directory. -func parentDirName(parentDirectoryBlock uint16, keyBlock uint16, subdirMap map[uint16]*Subdirectory) (string, error) { - if parentDirectoryBlock == keyBlock { +// parentDirName returns the full recursive directory name of the given parent directory. +func parentDirName(parentDirectoryBlock uint16, keyBlock uint16, subdirMap map[uint16]*Subdirectory, firstSubdirBlockMap map[uint16]uint16) (string, error) { + if parentDirectoryBlock == keyBlock || firstSubdirBlockMap[parentDirectoryBlock] == keyBlock { return "", nil } sd := subdirMap[parentDirectoryBlock] + if sd == nil { + parentFirstBlock, ok := firstSubdirBlockMap[parentDirectoryBlock] + if ok { + sd = subdirMap[parentFirstBlock] + } + } if sd == nil { return "", fmt.Errorf("Unable to find subdirectory for block %d", parentDirectoryBlock) } - parentName, err := parentDirName(sd.keyBlock.Header.ParentPointer, keyBlock, subdirMap) + + parentName, err := parentDirName(sd.keyBlock.Header.ParentPointer, keyBlock, subdirMap, firstSubdirBlockMap) if err != nil { return "", err } @@ -807,10 +852,12 @@ func (o operator) HasSubdirs() bool { // Catalog returns a catalog of disk entries. subdir should be empty // for operating systems that do not support subdirectories. func (o operator) Catalog(subdir string) ([]types.Descriptor, error) { - - vol, err := readVolume(o.data, 2) + if o.debug { + fmt.Fprintf(os.Stderr, "Catalog of %q\n", subdir) + } + vol, err := readVolume(o.data, 2, o.debug) if err != nil { - return nil, err + return nil, fmt.Errorf("error reading volume: %w", err) } var result []types.Descriptor @@ -855,6 +902,16 @@ func (o operator) PutFile(fileInfo types.FileInfo, overwrite bool) (existed bool return false, fmt.Errorf("%s doesn't implement PutFile yet", operatorName) } +// DiskOrder returns the Physical-to-Logical mapping order. +func (o operator) DiskOrder() types.DiskOrder { + return types.DiskOrderPO +} + +// GetBytes returns the disk image bytes, in logical order. +func (o operator) GetBytes() []byte { + return o.data +} + // OperatorFactory is a types.OperatorFactory for ProDos disks. type OperatorFactory struct { } @@ -868,14 +925,7 @@ func (of OperatorFactory) Name() string { // system of this operator. func (of OperatorFactory) SeemsToMatch(devicebytes []byte, debug bool) bool { // For now, just return true if we can run Catalog successfully. - if len(devicebytes) == disk.FloppyDiskBytes { - swizzled, err := of.swizzle(devicebytes) - if err != nil { - return false - } - devicebytes = swizzled - } - _, err := readVolume(devicebytes, 2) + _, err := readVolume(devicebytes, 2, debug) if err != nil { return false } @@ -884,16 +934,10 @@ func (of OperatorFactory) SeemsToMatch(devicebytes []byte, debug bool) bool { // Operator returns an Operator for the []byte disk image. func (of OperatorFactory) Operator(devicebytes []byte, debug bool) (types.Operator, error) { - if len(devicebytes) == disk.FloppyDiskBytes { - swizzled, err := of.swizzle(devicebytes) - if err != nil { - return nil, err - } - devicebytes = swizzled - } return operator{data: devicebytes, debug: debug}, nil } -func (of OperatorFactory) swizzle(rawbytes []byte) ([]byte, error) { - return disk.Swizzle(rawbytes, disk.ProDosPhysicalToLogicalSectorMap) +// DiskOrder returns the Physical-to-Logical mapping order. +func (of OperatorFactory) DiskOrder() types.DiskOrder { + return operator{}.DiskOrder() } diff --git a/script_test.go b/script_test.go new file mode 100644 index 0000000..a7ec304 --- /dev/null +++ b/script_test.go @@ -0,0 +1,25 @@ +package main + +import ( + "os" + "testing" + + "github.com/rogpeppe/go-internal/testscript" +) + +func testscriptMain() int { + main() + return 0 +} + +func TestMain(m *testing.M) { + os.Exit(testscript.RunMain(m, map[string]func() int{ + "diskii": testscriptMain, + })) +} + +func TestFoo(t *testing.T) { + testscript.Run(t, testscript.Params{ + Dir: "testdata", + }) +} diff --git a/supermon/supermon.go b/supermon/supermon.go index a62028b..dbe90a6 100644 --- a/supermon/supermon.go +++ b/supermon/supermon.go @@ -640,35 +640,35 @@ func (st SymbolTable) FilesForCompoundName(filename string) (numFile byte, named return numFile, namedFile, parts[1], nil } -// operator is a disk.Operator - an interface for performing +// Operator is a disk.Operator - an interface for performing // high-level operations on files and directories. -type operator struct { +type Operator struct { data []byte SM SectorMap ST SymbolTable debug bool } -var _ types.Operator = operator{} +var _ types.Operator = Operator{} // operatorName is the keyword name for the operator that undestands // NakedOS/Super-Mon disks. const operatorName = "nakedos" // Name returns the name of the Operator. -func (o operator) Name() string { +func (o Operator) Name() string { return operatorName } // HasSubdirs returns true if the underlying operating system on the // disk allows subdirectories. -func (o operator) HasSubdirs() bool { +func (o Operator) HasSubdirs() bool { return false } // Catalog returns a catalog of disk entries. subdir should be empty // for operating systems that do not support subdirectories. -func (o operator) Catalog(subdir string) ([]types.Descriptor, error) { +func (o Operator) Catalog(subdir string) ([]types.Descriptor, error) { var descs []types.Descriptor sectorsByFile := o.SM.SectorsByFile() for file := byte(1); file < FileReserved; file++ { @@ -689,7 +689,7 @@ func (o operator) Catalog(subdir string) ([]types.Descriptor, error) { } // GetFile retrieves a file by name. -func (o operator) GetFile(filename string) (types.FileInfo, error) { +func (o Operator) GetFile(filename string) (types.FileInfo, error) { file, err := o.ST.FileForName(filename) if err != nil { return types.FileInfo{}, err @@ -720,7 +720,7 @@ func (o operator) GetFile(filename string) (types.FileInfo, error) { // Delete deletes a file by name. It returns true if the file was // deleted, false if it didn't exist. -func (o operator) Delete(filename string) (bool, error) { +func (o Operator) Delete(filename string) (bool, error) { file, err := o.ST.FileForName(filename) if err != nil { return false, err @@ -744,9 +744,9 @@ func (o operator) Delete(filename string) (bool, error) { // PutFile writes a file by name. If the file exists and overwrite // is false, it returns with an error. Otherwise it returns true if // an existing file was overwritten. -func (o operator) PutFile(fileInfo types.FileInfo, overwrite bool) (existed bool, err error) { +func (o Operator) PutFile(fileInfo types.FileInfo, overwrite bool) (existed bool, err error) { if fileInfo.Descriptor.Type != types.FiletypeBinary { - return false, fmt.Errorf("%s: only binary file type supported", operatorName) + return false, fmt.Errorf("%s: only binary file type supported; got %q", operatorName, fileInfo.Descriptor.Type) } if fileInfo.Descriptor.Length != len(fileInfo.Data) { return false, fmt.Errorf("mismatch between FileInfo.Descriptor.Length (%d) and actual length of FileInfo.Data field (%d)", fileInfo.Descriptor.Length, len(fileInfo.Data)) @@ -785,25 +785,14 @@ func (o operator) PutFile(fileInfo types.FileInfo, overwrite bool) (existed bool return existed, nil } -// XOperatorFactory is the factory that returns supermon operators -// given disk images. -func XOperatorFactory(diskbytes []byte) (types.Operator, error) { - sm, err := LoadSectorMap(diskbytes) - if err != nil { - return nil, err - } - if err := sm.Verify(); err != nil { - return nil, err - } - - op := operator{data: diskbytes, SM: sm} - - st, err := sm.ReadSymbolTable(diskbytes) - if err == nil { - op.ST = st - } +// DiskOrder returns the Physical-to-Logical mapping order. +func (o Operator) DiskOrder() types.DiskOrder { + return types.DiskOrderRaw +} - return op, nil +// GetBytes returns the disk image bytes, in logical order. +func (o Operator) GetBytes() []byte { + return o.data } // OperatorFactory is a types.OperatorFactory for DOS 3.3 disks. @@ -839,7 +828,7 @@ func (of OperatorFactory) Operator(diskbytes []byte, debug bool) (types.Operator return nil, err } - op := operator{data: diskbytes, SM: sm, debug: debug} + op := Operator{data: diskbytes, SM: sm, debug: debug} st, err := sm.ReadSymbolTable(diskbytes) if err == nil { @@ -848,3 +837,8 @@ func (of OperatorFactory) Operator(diskbytes []byte, debug bool) (types.Operator return op, nil } + +// DiskOrder returns the Physical-to-Logical mapping order. +func (of OperatorFactory) DiskOrder() types.DiskOrder { + return Operator{}.DiskOrder() +} diff --git a/supermon/supermon_test.go b/supermon/supermon_test.go index eaeec0c..ac63cc6 100644 --- a/supermon/supermon_test.go +++ b/supermon/supermon_test.go @@ -52,9 +52,7 @@ func getOperator(filename string) (types.Operator, error) { if err != nil { return nil, err } - op, _, err := disk.OpenImage(f, "do", "nakedos", &types.Globals{ - DiskOperatorFactories: []types.OperatorFactory{OperatorFactory{}}, - }) + op, _, err := disk.OpenFile(f, "do", "nakedos", []types.OperatorFactory{OperatorFactory{}}, false) if err != nil { return nil, err } diff --git a/testdata/cathello.txt b/testdata/cathello.txt new file mode 100644 index 0000000..6ab0d8b --- /dev/null +++ b/testdata/cathello.txt @@ -0,0 +1,7 @@ +# hello world +exec cat hello.text +stdout 'hello world\n' +! stderr . + +-- hello.text -- +hello world \ No newline at end of file diff --git a/types/ops.go b/types/ops.go index 48c17f6..7af9d4d 100644 --- a/types/ops.go +++ b/types/ops.go @@ -17,11 +17,23 @@ type Descriptor struct { Type Filetype } +type DiskOrder string + +const ( + DiskOrderDO = DiskOrder("do") + DiskOrderPO = DiskOrder("po") + DiskOrderRaw = DiskOrder("raw") + DiskOrderAuto = DiskOrder("auto") + DiskOrderUnknown = DiskOrder("") +) + // OperatorFactory is the interface for getting operators, and finding out a bit // about them before getting them. type OperatorFactory interface { // Name returns the name of the operator. Name() string + // DiskOrder returns the Physical-to-Logical mapping order. + DiskOrder() DiskOrder // SeemsToMatch returns true if the []byte disk image seems to match the // system of this operator. SeemsToMatch(diskbytes []byte, debug bool) bool @@ -33,6 +45,8 @@ type OperatorFactory interface { type Operator interface { // Name returns the name of the operator. Name() string + // DiskOrder returns the Physical-to-Logical mapping order. + DiskOrder() DiskOrder // HasSubdirs returns true if the underlying operating system on the // disk allows subdirectories. HasSubdirs() bool @@ -48,6 +62,8 @@ type Operator interface { // is false, it returns with an error. Otherwise it returns true if // an existing file was overwritten. PutFile(fileInfo FileInfo, overwrite bool) (existed bool, err error) + // GetBytes returns the disk image bytes, in logical order. + GetBytes() []byte } // FileInfo represents a file descriptor plus the content.