diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..e2601bb --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,15 @@ +linters: + enable: + - gocritic + - godot + - gofmt + - gosec + - ifshort + - misspell + - nakedret + - nilerr + - predeclared + - revive + - stylecheck + - unconvert + - wastedassign diff --git a/basic/applesoft/applesoft.go b/basic/applesoft/applesoft.go index 27d047a..afd80ae 100644 --- a/basic/applesoft/applesoft.go +++ b/basic/applesoft/applesoft.go @@ -169,7 +169,7 @@ func Decode(raw []byte, location uint16) (Listing, error) { ofs += 2 for { if ofs >= len(raw) { - return nil, fmt.Errorf("Ran out of input at location $%X in line %d", ofs+int(location), line.Num) + return nil, fmt.Errorf("ran out of input at location $%X in line %d", ofs+int(location), line.Num) } char := raw[ofs] if char == 0 { diff --git a/basic/integer/integer.go b/basic/integer/integer.go index f5376da..b51a412 100644 --- a/basic/integer/integer.go +++ b/basic/integer/integer.go @@ -228,7 +228,7 @@ func (l Line) String() string { break } } else { - ch = ch - 0x80 + ch -= 0x80 if !lastAN && ch >= '0' && ch <= '9' { if len(l.Bytes) < i+3 { buf.WriteByte('?') diff --git a/cmd/catalog.go b/cmd/catalog.go index ae55392..947c4d1 100644 --- a/cmd/catalog.go +++ b/cmd/catalog.go @@ -38,7 +38,7 @@ func (l *LsCmd) Run(globals *types.Globals) error { if l.Directory != "" { if !op.HasSubdirs() { - return fmt.Errorf("Disks of type %q cannot have subdirectories", op.Name()) + return fmt.Errorf("disks of type %q cannot have subdirectories", op.Name()) } } fds, err := op.Catalog(l.Directory) diff --git a/data/data.go b/data/data.go index 6f035e5..69d8268 100644 --- a/data/data.go +++ b/data/data.go @@ -2,24 +2,24 @@ package data import _ "embed" -// DOS 3.3 Master Disk. +// DOS33masterDSK is a DOS 3.3 Master Disk image. //go:embed disks/dos33master.dsk -var DOS33master_dsk []byte +var DOS33masterDSK []byte -// DOS 3.3 Master Disk, as a .woz file. +// DOS33masterWOZ is a DOS 3.3 Master Disk, as a .woz file. //go:embed disks/dos33master.woz -var DOS33master_woz []byte +var DOS33masterWOZ []byte -// John Brooks' update to ProDOS. +// ProDOS242PO is John Brooks' update to ProDOS. // Website: https://prodos8.com // Announcements: https://www.callapple.org/author/jbrooks/ //go:embed disks/ProDOS_2_4_2.po -var ProDOS242_po []byte +var ProDOS242PO []byte -// The new ProDOS sector 0, used on and after the IIGS System 4.0. Understands sparse PRODOS.SYSTEM files. +// ProDOSNewBootSector0 is the new ProDOS sector 0, used on and after the IIGS System 4.0. Understands sparse PRODOS.SYSTEM files. //go:embed boot/prodos-new-boot0.bin var ProDOSNewBootSector0 []byte -// The old ProDOS sector 0, used before the IIGS System 4.0 system disk. +// ProDOSOldBootSector0 is the old ProDOS sector 0, used before the IIGS System 4.0 system disk. //go:embed boot/prodos-old-boot0.bin var ProDOSOldBootSector0 []byte diff --git a/dos3/dos3.go b/dos3/dos3.go index 189cbaa..9560d97 100644 --- a/dos3/dos3.go +++ b/dos3/dos3.go @@ -397,7 +397,7 @@ func (fd *FileDesc) Contents(diskbytes []byte) ([]byte, error) { for nextTrack != 0 || nextSector != 0 { ts := disk.TrackSector{Track: nextTrack, Sector: nextSector} if seen[ts] { - return nil, fmt.Errorf("File %q tries to read TrackSector track=%d sector=%d twice", fd.FilenameString(), nextTrack, nextSector) + return nil, fmt.Errorf("file %q tries to read TrackSector track=%d sector=%d twice", fd.FilenameString(), nextTrack, nextSector) } seen[ts] = true tsl := TrackSectorList{} @@ -497,7 +497,7 @@ func readCatalogSectors(diskbytes []byte, debug bool) ([]CatalogSector, error) { return nil, err } if err := v.Validate(); err != nil { - return nil, fmt.Errorf("Invalid VTOC sector: %v", err) + return nil, fmt.Errorf("invalid VTOC sector: %v", err) } nextTrack := v.CatalogTrack @@ -599,7 +599,7 @@ func (o operator) fileForFilename(filename string) (FileDesc, error) { return fd, nil } } - return FileDesc{}, fmt.Errorf("Filename %q not found", filename) + return FileDesc{}, fmt.Errorf("filename %q not found", filename) } // GetFile retrieves a file by name. diff --git a/helpers/helpers.go b/helpers/helpers.go index 7e956d0..b487b4f 100644 --- a/helpers/helpers.go +++ b/helpers/helpers.go @@ -31,5 +31,5 @@ func WriteOutput(filename string, contents []byte, force bool) error { return fmt.Errorf("cannot overwrite file %q without --force (-f)", filename) } } - return os.WriteFile(filename, contents, 0666) + return os.WriteFile(filename, contents, 0600) } diff --git a/main.go b/main.go index ddd2f7f..2b3775a 100644 --- a/main.go +++ b/main.go @@ -58,8 +58,7 @@ func run() error { func main() { - err := run() - if err != nil { + if err := run(); err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } diff --git a/prodos/prodos.go b/prodos/prodos.go index 9cb4399..9ff5f0e 100644 --- a/prodos/prodos.go +++ b/prodos/prodos.go @@ -113,7 +113,7 @@ func readVolumeBitMap(devicebytes []byte, startBlock uint16) (VolumeBitMap, erro return nil, fmt.Errorf("cannot read block %d (device block %d) of Volume Bit Map: %v", i, vbm[i].GetBlock(), err) } } - return VolumeBitMap(vbm), nil + return vbm, nil } // Write writes the Volume Bit Map to a block device. @@ -148,7 +148,7 @@ func (dt *DateTime) fromBytes(b []byte) { dt.HM[1] = b[3] } -// Validate checks a DateTime for problems, returning a slice of errors +// Validate checks a DateTime for problems, returning a slice of errors. func (dt DateTime) Validate(fieldDescription string) (errors []error) { if dt.HM[0] >= 24 { errors = append(errors, fmt.Errorf("%s expects hour<24; got %d", fieldDescription, dt.HM[0])) @@ -209,7 +209,7 @@ func (vdkb VolumeDirectoryKeyBlock) Validate() (errors []error) { errors = append(errors, desc.Validate()...) } if vdkb.Extra != 0 { - errors = append(errors, fmt.Errorf("Expected last byte of Volume Directory Key Block == 0x0; got 0x%02x", vdkb.Extra)) + errors = append(errors, fmt.Errorf("expected last byte of Volume Directory Key Block == 0x0; got 0x%02x", vdkb.Extra)) } return errors } @@ -256,7 +256,7 @@ func (vdb VolumeDirectoryBlock) Validate() (errors []error) { errors = append(errors, desc.Validate()...) } if vdb.Extra != 0 { - errors = append(errors, fmt.Errorf("Expected last byte of Volume Directory Block == 0x0; got 0x%02x", vdb.Extra)) + errors = append(errors, fmt.Errorf("expected last byte of Volume Directory Block == 0x0; got 0x%02x", vdb.Extra)) } return errors } @@ -337,7 +337,7 @@ type FileDescriptor struct { FileType byte // ProDOS / SOS filetype KeyPointer uint16 // block number of key block for file BlocksUsed uint16 // Total number of blocks used including index blocks and data blocks. For a subdirectory, the number of directory blocks - Eof [3]byte // 3-byte offset of EOF from first byte. For sequential files, just the length + EOF [3]byte // 3-byte offset of EOF from first byte. For sequential files, just the length Creation DateTime // Date and time of of file creation Version byte MinVersion byte @@ -357,7 +357,7 @@ func (fd FileDescriptor) descriptor() types.Descriptor { desc := types.Descriptor{ Name: fd.Name(), Blocks: int(fd.BlocksUsed), - Length: int(fd.Eof[0]) + int(fd.Eof[1])<<8 + int(fd.Eof[2])<<16, + Length: int(fd.EOF[0]) + int(fd.EOF[1])<<8 + int(fd.EOF[2])<<16, Locked: false, // TODO(zellyn): use prodos-style access in types.Descriptor Type: types.Filetype(fd.FileType), } @@ -382,7 +382,7 @@ func (fd FileDescriptor) toBytes() []byte { buf[0x10] = fd.FileType binary.LittleEndian.PutUint16(buf[0x11:0x13], fd.KeyPointer) binary.LittleEndian.PutUint16(buf[0x13:0x15], fd.BlocksUsed) - copyBytes(buf[0x15:0x18], fd.Eof[:]) + copyBytes(buf[0x15:0x18], fd.EOF[:]) copyBytes(buf[0x18:0x1c], fd.Creation.toBytes()) buf[0x1c] = fd.Version buf[0x1d] = fd.MinVersion @@ -403,7 +403,7 @@ func (fd *FileDescriptor) fromBytes(buf []byte) { fd.FileType = buf[0x10] fd.KeyPointer = binary.LittleEndian.Uint16(buf[0x11:0x13]) fd.BlocksUsed = binary.LittleEndian.Uint16(buf[0x13:0x15]) - copyBytes(fd.Eof[:], buf[0x15:0x18]) + copyBytes(fd.EOF[:], buf[0x15:0x18]) fd.Creation.fromBytes(buf[0x18:0x1c]) fd.Version = buf[0x1c] fd.MinVersion = buf[0x1d] @@ -485,7 +485,7 @@ func (skb SubdirectoryKeyBlock) Validate() (errors []error) { errors = append(errors, desc.Validate()...) } if skb.Extra != 0 { - errors = append(errors, fmt.Errorf("Expected last byte of Subdirectory Key Block == 0x0; got 0x%02x", skb.Extra)) + errors = append(errors, fmt.Errorf("expected last byte of Subdirectory Key Block == 0x0; got 0x%02x", skb.Extra)) } return errors } @@ -532,7 +532,7 @@ func (sb SubdirectoryBlock) Validate() (errors []error) { errors = append(errors, desc.Validate()...) } if sb.Extra != 0 { - errors = append(errors, fmt.Errorf("Expected last byte of Subdirectory Block == 0x0; got 0x%02x", sb.Extra)) + errors = append(errors, fmt.Errorf("expected last byte of Subdirectory Block == 0x0; got 0x%02x", sb.Extra)) } return errors } @@ -598,7 +598,7 @@ func (sh *SubdirectoryHeader) fromBytes(buf []byte) { // Validate validates a SubdirectoryHeader for valid values. func (sh SubdirectoryHeader) Validate() (errors []error) { if sh.SeventyFive != 0x75 { - errors = append(errors, fmt.Errorf("Byte after subdirectory name %q should be 0x75; got 0x%02x", sh.Name(), sh.SeventyFive)) + errors = append(errors, fmt.Errorf("byte after subdirectory name %q should be 0x75; got 0x%02x", sh.Name(), sh.SeventyFive)) } errors = append(errors, sh.Creation.Validate(fmt.Sprintf("subdirectory %q header creation date/time", sh.Name()))...) return errors @@ -669,12 +669,13 @@ func readVolume(devicebytes []byte, keyBlock uint16, debug bool) (Volume, error) // fmt.Fprintf(os.Stderr, "keyblock: %#v\n", v.keyBlock) // } - if vbm, err := readVolumeBitMap(devicebytes, v.keyBlock.Header.BitMapPointer); err != nil { + vbm, err := readVolumeBitMap(devicebytes, v.keyBlock.Header.BitMapPointer) + if err != nil { return v, err - } else { - v.bitmap = &vbm } + v.bitmap = &vbm + // if debug { // fmt.Fprintf(os.Stderr, "volume bitmap: %#v\n", v.bitmap) // } @@ -780,7 +781,7 @@ func parentDirName(parentDirectoryBlock uint16, keyBlock uint16, subdirMap map[u } } if sd == nil { - return "", fmt.Errorf("Unable to find subdirectory for block %d", parentDirectoryBlock) + return "", fmt.Errorf("unable to find subdirectory for block %d", parentDirectoryBlock) } parentName, err := parentDirName(sd.keyBlock.Header.ParentPointer, keyBlock, subdirMap, firstSubdirBlockMap) diff --git a/supermon/supermon.go b/supermon/supermon.go index 51942c9..b6a2199 100644 --- a/supermon/supermon.go +++ b/supermon/supermon.go @@ -75,10 +75,7 @@ func (sm SectorMap) Persist(diskbytes []byte) error { if err := disk.WriteSector(diskbytes, 0, 0xA, sm[0x30:0x130]); err != nil { return err } - if err := disk.WriteSector(diskbytes, 0, 0xB, sm[0x130:0x230]); err != nil { - return err - } - return nil + return disk.WriteSector(diskbytes, 0, 0xB, sm[0x130:0x230]) } // FreeSectors returns the number of blocks free in a sector map. @@ -96,7 +93,7 @@ func (sm SectorMap) FreeSectors() int { func (sm SectorMap) Verify() error { for sector := byte(0); sector <= 0xB; sector++ { if file := sm.FileForSector(0, sector); file != FileReserved { - return fmt.Errorf("Expected track 0, sectors 0-C to be reserved (0xFE), but got 0x%02X in sector %X", file, sector) + return fmt.Errorf("expected track 0, sectors 0-C to be reserved (0xFE), but got 0x%02X in sector %X", file, sector) } } @@ -104,7 +101,7 @@ func (sm SectorMap) Verify() error { for sector := byte(0); sector < 16; sector++ { file := sm.FileForSector(track, sector) if file == FileIllegal { - return fmt.Errorf("Found illegal sector map value (%02X), in track %X sector %X", FileIllegal, track, sector) + return fmt.Errorf("found illegal sector map value (%02X), in track %X sector %X", FileIllegal, track, sector) } } } @@ -254,14 +251,14 @@ func decodeSymbol(five []byte, extra byte) string { value := uint64(five[0]) + uint64(five[1])<<8 + uint64(five[2])<<16 + uint64(five[3])<<24 + uint64(five[4])<<32 + uint64(extra)<<40 for value&0x1f > 0 { if value&0x1f < 27 { - result = result + string(rune(value&0x1f+'@')) + result += string(rune(value&0x1f + '@')) value >>= 5 continue } if value&0x20 == 0 { - result = result + string(rune((value&0x1f)-0x1b+'0')) + result += string(rune((value & 0x1f) - 0x1b + '0')) } else { - result = result + string(rune((value&0x1f)-0x1b+'5')) + result += string(rune((value & 0x1f) - 0x1b + '5')) } value >>= 6 } @@ -345,10 +342,10 @@ func (sm SectorMap) ReadSymbolTable(diskbytes []byte) (SymbolTable, error) { link := -1 if linkAddr != 0 { if linkAddr < 0xD000 || linkAddr >= 0xDFFF { - return nil, fmt.Errorf("Expected symbol table link address between 0xD000 and 0xDFFE; got 0x%04X", linkAddr) + return nil, fmt.Errorf("expected symbol table link address between 0xD000 and 0xDFFE; got 0x%04X", linkAddr) } if (linkAddr-0xD000)%5 != 0 { - return nil, fmt.Errorf("Expected symbol table link address to 0xD000+5x; got 0x%04X", linkAddr) + return nil, fmt.Errorf("expected symbol table link address to 0xD000+5x; got 0x%04X", linkAddr) } link = (int(linkAddr) - 0xD000) / 5 } @@ -583,6 +580,7 @@ func (st SymbolTable) ParseCompoundSymbol(name string) (address uint16, symAddre } // If it's a valid symbol name, assume that's what it is. if _, err := encodeSymbol(name); err != nil { + //nolint:nilerr return 0, 0, name, nil } return 0, 0, "", fmt.Errorf("%q is not a valid symbol name or address", name) @@ -622,6 +620,7 @@ func (st SymbolTable) FilesForCompoundName(filename string) (numFile byte, named } file, err := st.FileForName(filename) if err != nil { + //nolint:nilerr return 0, 0, filename, nil } return file, file, filename, nil @@ -635,6 +634,7 @@ func (st SymbolTable) FilesForCompoundName(filename string) (numFile byte, named } namedFile, err = st.FileForName(parts[1]) if err != nil { + //nolint:nilerr return numFile, 0, parts[1], nil } return numFile, namedFile, parts[1], nil diff --git a/supermon/supermon_test.go b/supermon/supermon_test.go index ac63cc6..3fdffb8 100644 --- a/supermon/supermon_test.go +++ b/supermon/supermon_test.go @@ -17,7 +17,7 @@ const testDisk = "testdata/chacha20.dsk" const cities = `It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way - in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.` -// The extra newline pads us to 256 bytes… +// The extra newline pads us to 256 bytes. const hamlet = `To be, or not to be, that is the question: Whether 'tis Nobler in the mind to suffer The Slings and Arrows of outrageous Fortune, @@ -154,9 +154,7 @@ func TestGetFile(t *testing.T) { if err != nil { t.Fatal(err) } - got := string(file.Data) - want := hamlet - if got != want { + if want, got := hamlet, string(file.Data); got != want { t.Errorf("Incorrect result for GetFile(\"TOBE\"): want %q; got %q", want, got) } } @@ -255,8 +253,7 @@ func TestPutFile(t *testing.T) { t.Fatal(err) } last := fds[len(fds)-1] - want := "DF0B:FNEWFILE" - if got := last.Fullname; got != want { + if want, got := "DF0B:FNEWFILE", last.Fullname; got != want { t.Fatalf("Want last file on disk's FullName=%q; got %q", want, got) } } diff --git a/types/filetype.go b/types/filetype.go index c013cae..0490c5d 100644 --- a/types/filetype.go +++ b/types/filetype.go @@ -55,6 +55,7 @@ const ( // | 12-18 | SOS | SOS reserved for future use // | 1C-BF | SOS | SOS reserved for future use // | C0-EE | ProDOS | ProDOS reserved for future use + // End. ) // FiletypeInfo holds name information about filetype constants. @@ -70,7 +71,7 @@ type FiletypeInfo struct { NamesString string // (Generated) the names usable for this filetype. } -// names of Filetype constants above +// Names of Filetype constants above. var filetypeInfos = []FiletypeInfo{ {Type: FiletypeTypeless, Name: "Typeless", Desc: "Typeless file"}, {Type: FiletypeBadBlocks, Name: "BadBlocks", Desc: "Bad blocks file"}, @@ -183,7 +184,7 @@ func FiletypeForName(name string) (Filetype, error) { return info.Type, nil } } - return 0, fmt.Errorf("Unknown Filetype: %q", name) + return 0, fmt.Errorf("unknown Filetype: %q", name) } // FiletypeInfos returns a list information on all filetypes. diff --git a/woz/woz.go b/woz/woz.go index 4ae4005..cd08f0d 100644 --- a/woz/woz.go +++ b/woz/woz.go @@ -21,7 +21,7 @@ type Woz struct { } type UnknownChunk struct { - Id string + ID string Data []byte } @@ -80,14 +80,14 @@ func (e CRCError) Error() string { func (d *decoder) info(format string, args ...interface{}) { if !strings.HasSuffix(format, "\n") { - format = format + "\n" + format += "\n" } fmt.Printf("INFO: "+format, args...) } func (d *decoder) warn(format string, args ...interface{}) { if !strings.HasSuffix(format, "\n") { - format = format + "\n" + format += "\n" } fmt.Printf("WARN: "+format, args...) } @@ -100,10 +100,7 @@ func (d *decoder) checkHeader() error { if string(d.tmp[:len(wozHeader)]) != wozHeader { return FormatError("not a woz file") } - if err := binary.Read(d.r, binary.LittleEndian, &d.crcVal); err != nil { - return err - } - return nil + return binary.Read(d.r, binary.LittleEndian, &d.crcVal) } func (d *decoder) parseChunk() (done bool, err error) { @@ -233,7 +230,7 @@ func (d *decoder) parseUnknown(id string, length uint32) error { return err } d.crc.Write(buf) - d.woz.Unknowns = append(d.woz.Unknowns, UnknownChunk{Id: id, Data: buf}) + d.woz.Unknowns = append(d.woz.Unknowns, UnknownChunk{ID: id, Data: buf}) return nil } diff --git a/woz/woz_test.go b/woz/woz_test.go index 6e717d1..1a4544e 100644 --- a/woz/woz_test.go +++ b/woz/woz_test.go @@ -9,7 +9,7 @@ import ( ) func TestBasicLoad(t *testing.T) { - wz, err := woz.Decode(bytes.NewReader(data.DOS33master_woz)) + wz, err := woz.Decode(bytes.NewReader(data.DOS33masterWOZ)) if err != nil { t.Fatal(err) }