diff --git a/beeb_fstest_test.go b/beeb_fstest_test.go index 95a27dd..2bd0b7c 100644 --- a/beeb_fstest_test.go +++ b/beeb_fstest_test.go @@ -20,5 +20,5 @@ func Test_beeb_ftest(t *testing.T) { } os.Remove("+.0") - os.Remove("+.0.inf") + os.Remove("+.0" + metadataExtension) } diff --git a/console.go b/console.go index bd9c218..08fc1c3 100644 --- a/console.go +++ b/console.go @@ -9,7 +9,8 @@ import ( type console interface { readline() (string, bool) readChar() (uint8, bool) - write(s string) + write(string) + writef(string, ...interface{}) close() } @@ -51,4 +52,9 @@ func (c *consoleSimple) write(s string) { c.env.writeSpool(s) } +func (c *consoleSimple) writef(format string, a ...interface{}) { + s := fmt.Sprintf(format, a...) + c.write(s) +} + func (c *consoleSimple) close() {} diff --git a/consoleLiner.go b/consoleLiner.go index 1d9b701..e34d68a 100644 --- a/consoleLiner.go +++ b/consoleLiner.go @@ -88,3 +88,8 @@ func (c *consoleLiner) write(s string) { fmt.Print(s) c.env.writeSpool(s) } + +func (c *consoleLiner) writef(format string, a ...interface{}) { + s := fmt.Sprintf(format, a...) + c.write(s) +} diff --git a/consoleMock.go b/consoleMock.go index 3e4686e..f606554 100644 --- a/consoleMock.go +++ b/consoleMock.go @@ -1,5 +1,7 @@ package main +import "fmt" + type consoleMock struct { linesIn []string lineIn int @@ -40,4 +42,9 @@ func (c *consoleMock) write(s string) { c.env.writeSpool(s) } +func (c *consoleMock) writef(format string, a ...interface{}) { + s := fmt.Sprintf(format, a...) + c.write(s) +} + func (c *consoleMock) close() {} diff --git a/constants.go b/constants.go index 9977eab..0fa7d95 100644 --- a/constants.go +++ b/constants.go @@ -110,4 +110,6 @@ const ( // Maximim delay to detect a double control C to quit controlCDelayToQuitMs = 500 + + metadataExtension = ".inf" ) diff --git a/environment.go b/environment.go index 0038819..c821d61 100644 --- a/environment.go +++ b/environment.go @@ -105,7 +105,7 @@ func (env *environment) initLanguage(slot uint8) { The MOS also automatically prints the ROM's title string (&8009) so that the user is acknowledged. */ language := env.mem.peekString(romTitleString, 0) - env.con.write(fmt.Sprintf("%s\n\n", language)) + env.con.writef("%s\n\n", language) _, x, y, p := env.cpu.GetAXYP() env.cpu.SetAXYP(1, x, y, p) diff --git a/osCLI.go b/osCLI.go index 3bfa042..ea9186c 100644 --- a/osCLI.go +++ b/osCLI.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "os/exec" + "path" "strconv" "strings" ) @@ -24,6 +25,7 @@ var cliCommands = []string{ "DELETE", "DRIVE", "EXEC", + "EX", "HELP", "HOST", // Added for bbz "INFO", @@ -117,7 +119,61 @@ func execOSCLI(env *environment) { } case "CAT": - env.con.write("\n") + pathName := "" + _, pathName, valid = parseFilename(line, pos) + if !valid { + env.raiseError(254, "Bad Command") + break + } + if pathName == "" || pathName == "$" { + pathName = "." + } + + entries, err := os.ReadDir(pathName) + if err != nil { + env.raiseError(errorTodo, err.Error()) + break + } + + for _, entry := range entries { + if strings.HasPrefix(entry.Name(), ".") || + strings.HasSuffix(entry.Name(), metadataExtension) { + // Ignore the file + } else if entry.Type().IsRegular() { + env.con.writef("%v\n", entry.Name()) + } else { + // Ignore the file + } + } + + case "EX": + pathName := "" + _, pathName, valid = parseFilename(line, pos) + if !valid { + env.raiseError(254, "Bad Command") + break + } + if pathName == "" || pathName == "$" { + pathName = "." + } + + entries, err := os.ReadDir(pathName) + if err != nil { + env.raiseError(errorTodo, err.Error()) + break + } + + for _, entry := range entries { + if entry.Type().IsRegular() { + fullName := path.Join(pathName, entry.Name()) + attr := getFileAttributes(env, fullName) + env.con.writef("%-22v %06x %06x %06x \n", + entry.Name(), + attr.loadAddress&0xff_ffff, + attr.executionAddress&0xff_ffff, + attr.fileSize&0xff_ffff) + } + } case "CODE": execOSCLIfx(env, 0x88, line, pos) @@ -206,10 +262,10 @@ func execOSCLI(env *environment) { attr := getFileAttributes(env, filename) if attr.hasMetadata { - env.con.write(fmt.Sprintf("%s\t %06X %06X %06X\n", filename, - attr.loadAddress, attr.executionAddress, attr.fileSize)) + env.con.writef("%s\t %06X %06X %06X\n", filename, + attr.loadAddress, attr.executionAddress, attr.fileSize) } else { - env.con.write(fmt.Sprintf("%s\t ?????? ?????? %06X\n", filename, attr.fileSize)) + env.con.writef("%s\t ?????? ?????? %06X\n", filename, attr.fileSize) } // case "KEY": @@ -267,7 +323,7 @@ func execOSCLI(env *environment) { env.mem.Poke(sheilaRomLatch, uint8(i)) name := env.mem.peekString(romTitleString, 0) if name == "" { - env.con.write(fmt.Sprintf("ROM %X ?\n", i)) + env.con.writef("ROM %X ?\n", i) } else { version := env.mem.Peek(romVersion) romType := env.mem.Peek(romTypeByte) @@ -280,10 +336,10 @@ func execOSCLI(env *environment) { } attributes += ")" - env.con.write(fmt.Sprintf("ROM %X %s %02v %s\n", i, name, version, attributes)) + env.con.writef("ROM %X %s %02v %s\n", i, name, version, attributes) } } else { - env.con.write(fmt.Sprintf("RAM %X 16K\n", i)) + env.con.writef("RAM %X 16K\n", i) } } env.mem.Poke(sheilaRomLatch, currentRom) diff --git a/osFile.go b/osFile.go index 61a9a04..773eb00 100644 --- a/osFile.go +++ b/osFile.go @@ -257,7 +257,7 @@ func deleteFile(env *environment, filename string) uint8 { return osNotFound } - os.Remove(filename + ".inf") + os.Remove(filename + metadataExtension) return osFileFound } @@ -285,33 +285,34 @@ func getFileAttributes(env *environment, filename string) *fileAttributes { $.BasObj 003000 003100 005000 00 CRC32=614721E1 */ attr.hasMetadata = false - data, err := os.ReadFile(filename + ".inf") + metadata := filename + metadataExtension + data, err := os.ReadFile(metadata) if errors.Is(err, os.ErrNotExist) { return &attr } parts := strings.Fields(string(data)) if len(parts) < 5 { - env.log(fmt.Sprintf("Invalid format for metadata file %s.inf, missing fields", filename)) + env.log(fmt.Sprintf("Invalid format for metadata file for %s, missing fields", metadata)) return &attr } i, err := strconv.ParseUint(parts[1], 16, 64) if err != nil { - env.log(fmt.Sprintf("Invalid format for metadata file %s.inf, bad load address '%s'", filename, err.Error())) + env.log(fmt.Sprintf("Invalid format for metadata file %s, bad load address '%s'", metadata, err.Error())) return &attr } attr.loadAddress = uint32(i) i, err = strconv.ParseUint(parts[2], 16, 64) if err != nil { - env.log(fmt.Sprintf("Invalid format for metadata file %s.inf, bad exec address '%s'", filename, err.Error())) + env.log(fmt.Sprintf("Invalid format for metadata file %s, bad exec address '%s'", metadata, err.Error())) return &attr } attr.executionAddress = uint32(i) i, err = strconv.ParseUint(parts[4], 16, 64) if err != nil { - env.log(fmt.Sprintf("Invalid format for metadata file %s.inf, bad sttributes '%s'", filename, err.Error())) + env.log(fmt.Sprintf("Invalid format for metadata file %s, bad sttributes '%s'", metadata, err.Error())) return &attr } attr.attributes = uint32(i) @@ -324,5 +325,5 @@ func writeMetada(env *environment, filename string, attr *fileAttributes) { // $.BasObj 003000 003100 005000 00 CRC32=614721E1 metadata := fmt.Sprintf("$.FILE %08X %08X %08X %02X", attr.loadAddress, attr.executionAddress, attr.fileSize, attr.attributes) - os.WriteFile(filename+".inf", []byte(metadata), 0644) + os.WriteFile(filename+metadataExtension, []byte(metadata), 0644) }