mirror of
https://github.com/zellyn/diskii.git
synced 2024-11-25 19:31:46 +00:00
Add stubbed-out delete, supermon symbol encoding
This commit is contained in:
parent
e6508a39b4
commit
0fdf6f05c4
68
cmd/delete.go
Normal file
68
cmd/delete.go
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright © 2016 Zellyn Hunter <zellyn@gmail.com>
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/zellyn/diskii/lib/disk"
|
||||
_ "github.com/zellyn/diskii/lib/dos3"
|
||||
_ "github.com/zellyn/diskii/lib/supermon"
|
||||
)
|
||||
|
||||
// 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)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(deleteCmd)
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
sd, err := disk.Open(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
op, err := disk.OperatorFor(sd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
deleted, err := op.Delete(args[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !deleted {
|
||||
// TODO(zellyn): implement -f flag to not warn on nonexistence.
|
||||
return fmt.Errorf("file %q not found", args[1])
|
||||
}
|
||||
f, err := os.Create(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = sd.Write(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = f.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
@ -6,6 +6,7 @@ package disk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
@ -49,6 +50,8 @@ type SectorDisk interface {
|
||||
Sectors() byte
|
||||
// Tracks returns the number of tracks on the SectorDisk
|
||||
Tracks() byte
|
||||
// Write writes the disk contents to the given file.
|
||||
Write(io.Writer) (int, error)
|
||||
}
|
||||
|
||||
type LogicalSectorDisk interface {
|
||||
@ -62,6 +65,8 @@ type LogicalSectorDisk interface {
|
||||
Sectors() byte
|
||||
// Tracks returns the number of tracks on the SectorDisk
|
||||
Tracks() byte
|
||||
// Write writes the disk contents to the given file.
|
||||
Write(io.Writer) (int, error)
|
||||
}
|
||||
|
||||
// MappedDisk wraps a SectorDisk as a LogicalSectorDisk, handling the
|
||||
@ -125,6 +130,11 @@ func (md MappedDisk) Tracks() byte {
|
||||
return md.sectorDisk.Tracks()
|
||||
}
|
||||
|
||||
// Write writes the disk contents to the given file.
|
||||
func (md MappedDisk) Write(w io.Writer) (n int, err error) {
|
||||
return md.sectorDisk.Write(w)
|
||||
}
|
||||
|
||||
// Open opens a disk image by filename.
|
||||
func Open(filename string) (SectorDisk, error) {
|
||||
ext := strings.ToLower(path.Ext(filename))
|
||||
|
@ -6,6 +6,7 @@ package disk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
@ -84,3 +85,8 @@ func (d DSK) Sectors() byte {
|
||||
func (d DSK) Tracks() byte {
|
||||
return d.tracks
|
||||
}
|
||||
|
||||
// Write writes the disk contents to the given file.
|
||||
func (d DSK) Write(w io.Writer) (n int, err error) {
|
||||
return w.Write(d.data)
|
||||
}
|
||||
|
@ -83,6 +83,9 @@ type Operator interface {
|
||||
Catalog(subdir string) ([]Descriptor, error)
|
||||
// GetFile retrieves a file by name.
|
||||
GetFile(filename string) (FileInfo, error)
|
||||
// Delete deletes a file by name. It returns true if the file was
|
||||
// deleted, false if it didn't exist.
|
||||
Delete(filename string) (bool, error)
|
||||
}
|
||||
|
||||
// FileInfo represents a file descriptor plus the content.
|
||||
|
@ -651,6 +651,12 @@ func (o operator) GetFile(filename string) (disk.FileInfo, error) {
|
||||
return disk.FileInfo{}, fmt.Errorf("%s does not yet implement `GetFile` for filetype %s", operatorName, errType)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return false, fmt.Errorf("%s does not implement Delete yet", operatorName)
|
||||
}
|
||||
|
||||
// operatorFactory is the factory that returns dos3 operators given
|
||||
// disk images.
|
||||
func operatorFactory(sd disk.SectorDisk) (disk.Operator, error) {
|
||||
|
@ -4,9 +4,8 @@
|
||||
// structures of NakedOS/Super-Mon disks.
|
||||
package supermon
|
||||
|
||||
// TODO(zellyn): remove panics.
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -152,6 +151,44 @@ func decodeSymbol(five []byte, extra byte) string {
|
||||
return result
|
||||
}
|
||||
|
||||
// encodeSymbol encodes a symbol name into the five+1 bytes used in a
|
||||
// Super-Mon encoded symbol table entry. The returned byte array will
|
||||
// always be six bytes long. If it can't be encoded, it returns an
|
||||
// error.
|
||||
func encodeSymbol(name string) (six []byte, err error) {
|
||||
if len(name) > 9 {
|
||||
return nil, fmt.Errorf("invalid Super-Mon symbol %q: too long", name)
|
||||
}
|
||||
if len(name) < 3 {
|
||||
return nil, fmt.Errorf("invalid Super-Mon symbol %q: too short", name)
|
||||
}
|
||||
nm := []byte(strings.ToUpper(name))
|
||||
value := uint64(0)
|
||||
bits := 0
|
||||
for i := len(nm) - 1; i >= 0; i-- {
|
||||
ch := nm[i]
|
||||
switch {
|
||||
case 'A' <= ch && ch <= 'Z':
|
||||
value = value<<5 + uint64(ch-'@')
|
||||
bits += 5
|
||||
case '0' <= ch && ch <= '4':
|
||||
value = value<<6 + 0x1b + uint64(ch-'0')
|
||||
bits += 6
|
||||
case '5' <= ch && ch <= '9':
|
||||
value = value<<6 + 0x3b + uint64(ch-'5')
|
||||
bits += 6
|
||||
}
|
||||
if bits > 48 {
|
||||
return nil, fmt.Errorf("invalid Super-Mon symbol %q: too long", name)
|
||||
}
|
||||
}
|
||||
eight := make([]byte, 8)
|
||||
six = make([]byte, 6)
|
||||
binary.LittleEndian.PutUint64(eight, value)
|
||||
copy(six, eight)
|
||||
return six, nil
|
||||
}
|
||||
|
||||
// SymbolTable represents an entire Super-Mon symbol table. It'll
|
||||
// always be 819 entries long, because it includes blanks.
|
||||
type SymbolTable []Symbol
|
||||
@ -192,7 +229,7 @@ func (sm SectorMap) ReadSymbolTable(sd disk.SectorDisk) (SymbolTable, error) {
|
||||
if (linkAddr-0xD000)%5 != 0 {
|
||||
return nil, fmt.Errorf("Expected symbol table link address to 0xD000+5x; got 0x%04X", linkAddr)
|
||||
}
|
||||
link = (int(linkAddr) - 0xD000) % 5
|
||||
link = (int(linkAddr) - 0xD000) / 5
|
||||
}
|
||||
extra := symtbl1[i+4]
|
||||
copy(five, symtbl2[i:i+5])
|
||||
@ -205,11 +242,27 @@ func (sm SectorMap) ReadSymbolTable(sd disk.SectorDisk) (SymbolTable, error) {
|
||||
table = append(table, symbol)
|
||||
}
|
||||
|
||||
// TODO(zellyn): check link addresses.
|
||||
for i, sym := range table {
|
||||
if sym.Address != 0 && sym.Link != -1 {
|
||||
if sym.Link == i {
|
||||
return nil, fmt.Errorf("Symbol %q (0x%04X) links to itself", sym.Name, sym.Address)
|
||||
}
|
||||
linkSym := table[sym.Link]
|
||||
if addrHash(sym.Address) != addrHash(linkSym.Address) {
|
||||
return nil, fmt.Errorf("Symbol %q (0x%04X) with hash 0x%02X links to symbol %q (0x%04X) with hash 0x%02X",
|
||||
sym.Name, sym.Address, addrHash(sym.Address), linkSym.Name, linkSym.Address, addrHash(linkSym.Address))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return table, nil
|
||||
}
|
||||
|
||||
// addrHash computes the SuperMon hash for an address.
|
||||
func addrHash(addr uint16) byte {
|
||||
return (byte(addr) ^ byte(addr>>8)) & 0x7f
|
||||
}
|
||||
|
||||
// SymbolsByAddress returns a map of addresses to slices of symbols.
|
||||
func (st SymbolTable) SymbolsByAddress() map[uint16][]Symbol {
|
||||
result := map[uint16][]Symbol{}
|
||||
@ -319,12 +372,21 @@ func (o operator) GetFile(filename string) (disk.FileInfo, error) {
|
||||
Length: len(data),
|
||||
Locked: false,
|
||||
Type: disk.FiletypeBinary,
|
||||
// TODO(zellyn): Set StartAddress if we know it.
|
||||
}
|
||||
return disk.FileInfo{
|
||||
fi := disk.FileInfo{
|
||||
Descriptor: desc,
|
||||
Data: data,
|
||||
}, nil
|
||||
}
|
||||
if file == 1 {
|
||||
fi.StartAddress = 0x1800
|
||||
}
|
||||
return fi, nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return false, fmt.Errorf("%s does not implement Delete yet", operatorName)
|
||||
}
|
||||
|
||||
// operatorFactory is the factory that returns supermon operators
|
||||
|
@ -3,6 +3,7 @@
|
||||
package supermon
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/zellyn/diskii/lib/disk"
|
||||
@ -137,3 +138,43 @@ No more; and by a sleep, to say we end
|
||||
t.Errorf("Incorrect result for GetFile(\"TOBE\"): want %q; got %q", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEncodeDecode tests encoding and decoding of Super-Mon symbol
|
||||
// table entries.
|
||||
func TestEncodeDecode(t *testing.T) {
|
||||
testdata := []struct {
|
||||
sym string
|
||||
valid bool
|
||||
}{
|
||||
{"ABC", true},
|
||||
{"abc", true},
|
||||
{"ABCDEFGHI", true},
|
||||
{"abcdefghi", true},
|
||||
{"ABCDEF123", true},
|
||||
{"abcdef123", true},
|
||||
|
||||
{"AB", false},
|
||||
{"ab", false},
|
||||
{"ABCDE1234", false},
|
||||
{"abcde1234", false},
|
||||
}
|
||||
|
||||
for _, tt := range testdata {
|
||||
if !tt.valid {
|
||||
if _, err := encodeSymbol(tt.sym); err == nil {
|
||||
t.Errorf("Expected symbol %q to be invalid, but wansn't", tt.sym)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
bytes, err := encodeSymbol(tt.sym)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error encoding symbol %q", tt.sym)
|
||||
continue
|
||||
}
|
||||
sym := decodeSymbol(bytes[:5], bytes[5])
|
||||
if sym != strings.ToUpper(tt.sym) {
|
||||
t.Errorf("Symbol %q encodes to %q", tt.sym, sym)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user