diff --git a/cmd/delete.go b/cmd/delete.go index b5ec26d..7c1733c 100644 --- a/cmd/delete.go +++ b/cmd/delete.go @@ -12,6 +12,8 @@ import ( _ "github.com/zellyn/diskii/lib/supermon" ) +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", @@ -30,6 +32,7 @@ delete disk-image.dsk HELLO func init() { RootCmd.AddCommand(deleteCmd) + deleteCmd.Flags().BoolVarP(&missingok, "missingok", "f", false, "if true, don't consider deleting a nonexistent file an error") } // runDelete performs the actual delete logic. @@ -49,8 +52,7 @@ func runDelete(args []string) error { if err != nil { return err } - if !deleted { - // TODO(zellyn): implement -f flag to not warn on nonexistence. + if !deleted && !missingok { return fmt.Errorf("file %q not found", args[1]) } f, err := os.Create(args[0]) diff --git a/cmd/filetypes.go b/cmd/filetypes.go new file mode 100644 index 0000000..768b2ac --- /dev/null +++ b/cmd/filetypes.go @@ -0,0 +1,45 @@ +// Copyright © 2016 Zellyn Hunter + +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" +) + +var all bool // flag for whether to show all filetypes + +// filetypesCmd represents the filetypes command, used to display +// valid filetypes recognized by diskii. +var filetypesCmd = &cobra.Command{ + Use: "filetypes", + Short: "print a list of filetypes", + Long: `Print a list of filetypes understood by diskii`, + Run: func(cmd *cobra.Command, args []string) { + if err := runFiletypes(args); err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(-1) + } + }, +} + +func init() { + RootCmd.AddCommand(filetypesCmd) + filetypesCmd.Flags().BoolVarP(&all, "all", "a", false, "display all types, including SOS types and reserved ranges") +} + +// runFiletypes performs the actual listing of filetypes. +func runFiletypes(args []string) error { + if len(args) != 0 { + return fmt.Errorf("filetypes expects no arguments") + } + for _, typ := range disk.FiletypeNames(all) { + fmt.Println(typ) + } + return nil +} diff --git a/cmd/put.go b/cmd/put.go index 888db14..f281cc7 100644 --- a/cmd/put.go +++ b/cmd/put.go @@ -13,6 +13,9 @@ import ( _ "github.com/zellyn/diskii/lib/supermon" ) +var filetypeName string // flag for file type +var overwrite bool // flag for whether to overwrite + // putCmd represents the put command, used to put the raw contents // of a file. var putCmd = &cobra.Command{ @@ -32,12 +35,14 @@ put disk-image.dsk HELLO 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") } // runPut performs the actual put logic. func runPut(args []string) error { if len(args) != 3 { - return fmt.Errorf("put expects a disk image filename, an disk-image filename, and a filename to read the contents from") + return fmt.Errorf("usage: put ") } sd, err := disk.Open(args[0]) if err != nil { @@ -52,5 +57,33 @@ func runPut(args []string) error { return err } + filetype, err := disk.FiletypeForName(filetypeName) + if err != nil { + return err + } + + fileInfo := disk.FileInfo{ + Descriptor: disk.Descriptor{ + Name: args[1], + 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 = sd.Write(f) + if err != nil { + return err + } + if err = f.Close(); err != nil { + return err + } return nil } diff --git a/lib/disk/filetype.go b/lib/disk/filetype.go index e921f3f..27ee6f8 100644 --- a/lib/disk/filetype.go +++ b/lib/disk/filetype.go @@ -48,8 +48,8 @@ const ( FiletypeRelocatable Filetype = 0xFE // REL | ProDOS | EDASM relocatable object module file FiletypeSystem Filetype = 0xFF // SYS | ProDOS | System file FiletypeS Filetype = 0x100 // DOS 3.3 Type "S" - FiletypeA Filetype = 0x101 // DOS 3.3 Type "new A" - FiletypeB Filetype = 0x102 // DOS 3.3 Type "new B" + FiletypeNewA Filetype = 0x101 // DOS 3.3 Type "new A" + FiletypeNewB Filetype = 0x102 // DOS 3.3 Type "new B" // | 0D-0E | SOS | SOS reserved for future use // | 12-18 | SOS | SOS reserved for future use // | 1C-BF | SOS | SOS reserved for future use @@ -64,52 +64,54 @@ type filetypeInfo struct { OneLetter string // The one-letter abbreviation (DOS 3.x) Desc string // The description of the type Stringified string // (Generated) result of calling String() on the Constant + Extra bool // If true, exclude from normal display listing } // names of Filetype constants above var filetypeInfos = []filetypeInfo{ - {FiletypeTypeless, "Typeless", "", "", "Typeless file", ""}, - {FiletypeBadBlocks, "BadBlocks", "", "", "Bad blocks file", ""}, - {FiletypeSOSPascalCode, "SOSPascalCode", "", "", "PASCAL code file", ""}, - {FiletypeSOSPascalText, "SOSPascalText", "", "", "PASCAL text file", ""}, - {FiletypeASCIIText, "ASCIIText", "T", "TXT", "ASCII text file", ""}, - {FiletypeSOSPascalText2, "SOSPascalText2", "", "", "PASCAL text file", ""}, - {FiletypeBinary, "Binary", "B", "BIN", "Binary file", ""}, - {FiletypeFont, "Font", "", "", "Font file", ""}, - {FiletypeGraphicsScreen, "GraphicsScreen", "", "", "Graphics screen file", ""}, - {FiletypeBusinessBASIC, "BusinessBASIC", "", "", "Business BASIC program file", ""}, - {FiletypeBusinessBASICData, "BusinessBASICData", "", "", "Business BASIC data file", ""}, - {FiletypeSOSWordProcessor, "SOSWordProcessor", "", "", "Word processor file", ""}, - {FiletypeSOSSystem, "SOSSystem", "", "", "SOS system file", ""}, - {FiletypeDirectory, "Directory", "", "DIR", "Directory file", ""}, - {FiletypeRPSData, "RPSData", "", "", "RPS data file", ""}, - {FiletypeRPSIndex, "RPSIndex", "", "", "RPS index file", ""}, - {FiletypeAppleWorksDatabase, "AppleWorksDatabase", "", "ADB", "AppleWorks data base file", ""}, - {FiletypeAppleWorksWordProcessor, "AppleWorksWordProcessor", "", "AWP", "AppleWorks word processing file", ""}, - {FiletypeAppleWorksSpreadsheet, "AppleWorksSpreadsheet", "", "ASP", "AppleWorks spreadsheet file", ""}, - {FiletypePascal, "Pascal", "", "PAS", "ProDOS PASCAL file", ""}, - {FiletypeCommand, "Command", "", "CMD", "Added command file", ""}, - {FiletypeUserDefinedF1, "UserDefinedF1", "", "", "ProDOS user defined file type F1", ""}, - {FiletypeUserDefinedF2, "UserDefinedF2", "", "", "ProDOS user defined file type F2", ""}, - {FiletypeUserDefinedF3, "UserDefinedF3", "", "", "ProDOS user defined file type F3", ""}, - {FiletypeUserDefinedF4, "UserDefinedF4", "", "", "ProDOS user defined file type F4", ""}, - {FiletypeUserDefinedF5, "UserDefinedF5", "", "", "ProDOS user defined file type F5", ""}, - {FiletypeUserDefinedF6, "UserDefinedF6", "", "", "ProDOS user defined file type F6", ""}, - {FiletypeUserDefinedF7, "UserDefinedF7", "", "", "ProDOS user defined file type F7", ""}, - {FiletypeUserDefinedF8, "UserDefinedF8", "", "", "ProDOS user defined file type F8", ""}, - {FiletypeIntegerBASIC, "IntegerBASIC", "I", "INT", "Integer BASIC program file", ""}, - {FiletypeIntegerBASICVariables, "IntegerBASICVariables", "", "IVR", "Integer BASIC variables file", ""}, - {FiletypeApplesoftBASIC, "ApplesoftBASIC", "A", "BAS", "Applesoft BASIC program file", ""}, - {FiletypeApplesoftBASICVariables, "ApplesoftBASICVariables", "", "VAR", "Applesoft BASIC variables file", ""}, - {FiletypeRelocatable, "Relocatable", "R", "REL", "EDASM relocatable object module file", ""}, - {FiletypeSystem, "System", "", "SYS", "System file", ""}, - {FiletypeS, "S", "S", "", `DOS 3.3 Type "S"`, ""}, - {FiletypeA, "A", "A", "", `DOS 3.3 Type "new A"`, ""}, - {FiletypeB, "B", "B", "", `DOS 3.3 Type "new B"`, ""}, + {FiletypeTypeless, "Typeless", "", "", "Typeless file", "", false}, + {FiletypeBadBlocks, "BadBlocks", "", "", "Bad blocks file", "", false}, + {FiletypeSOSPascalCode, "SOSPascalCode", "", "", "PASCAL code file", "", true}, + {FiletypeSOSPascalText, "SOSPascalText", "", "", "PASCAL text file", "", true}, + {FiletypeASCIIText, "ASCIIText", "T", "TXT", "ASCII text file", "", false}, + {FiletypeSOSPascalText2, "SOSPascalText2", "", "", "PASCAL text file", "", true}, + {FiletypeBinary, "Binary", "B", "BIN", "Binary file", "", false}, + {FiletypeFont, "Font", "", "", "Font file", "", true}, + {FiletypeGraphicsScreen, "GraphicsScreen", "", "", "Graphics screen file", "", true}, + {FiletypeBusinessBASIC, "BusinessBASIC", "", "", "Business BASIC program file", "", true}, + {FiletypeBusinessBASICData, "BusinessBASICData", "", "", "Business BASIC data file", "", true}, + {FiletypeSOSWordProcessor, "SOSWordProcessor", "", "", "Word processor file", "", true}, + {FiletypeSOSSystem, "SOSSystem", "", "", "SOS system file", "", true}, + {FiletypeDirectory, "Directory", "", "DIR", "Directory file", "", false}, + {FiletypeRPSData, "RPSData", "", "", "RPS data file", "", true}, + {FiletypeRPSIndex, "RPSIndex", "", "", "RPS index file", "", true}, + {FiletypeAppleWorksDatabase, "AppleWorksDatabase", "", "ADB", "AppleWorks data base file", "", false}, + {FiletypeAppleWorksWordProcessor, "AppleWorksWordProcessor", "", "AWP", "AppleWorks word processing file", "", false}, + {FiletypeAppleWorksSpreadsheet, "AppleWorksSpreadsheet", "", "ASP", "AppleWorks spreadsheet file", "", false}, + {FiletypePascal, "Pascal", "", "PAS", "ProDOS PASCAL file", "", false}, + {FiletypeCommand, "Command", "", "CMD", "Added command file", "", false}, + {FiletypeUserDefinedF1, "UserDefinedF1", "", "", "ProDOS user defined file type F1", "", true}, + {FiletypeUserDefinedF2, "UserDefinedF2", "", "", "ProDOS user defined file type F2", "", true}, + {FiletypeUserDefinedF3, "UserDefinedF3", "", "", "ProDOS user defined file type F3", "", true}, + {FiletypeUserDefinedF4, "UserDefinedF4", "", "", "ProDOS user defined file type F4", "", true}, + {FiletypeUserDefinedF5, "UserDefinedF5", "", "", "ProDOS user defined file type F5", "", true}, + {FiletypeUserDefinedF6, "UserDefinedF6", "", "", "ProDOS user defined file type F6", "", true}, + {FiletypeUserDefinedF7, "UserDefinedF7", "", "", "ProDOS user defined file type F7", "", true}, + {FiletypeUserDefinedF8, "UserDefinedF8", "", "", "ProDOS user defined file type F8", "", true}, + {FiletypeIntegerBASIC, "IntegerBASIC", "I", "INT", "Integer BASIC program file", "", false}, + {FiletypeIntegerBASICVariables, "IntegerBASICVariables", "", "IVR", "Integer BASIC variables file", "", false}, + {FiletypeApplesoftBASIC, "ApplesoftBASIC", "A", "BAS", "Applesoft BASIC program file", "", false}, + {FiletypeApplesoftBASICVariables, "ApplesoftBASICVariables", "", "VAR", "Applesoft BASIC variables file", "", false}, + {FiletypeRelocatable, "Relocatable", "R", "REL", "EDASM relocatable object module file", "", false}, + {FiletypeSystem, "System", "", "SYS", "System file", "", false}, + {FiletypeS, "S", "", "S", `DOS 3.3 Type "S"`, "", false}, + {FiletypeNewA, "NewA", "", "A", `DOS 3.3 Type "new A"`, "", false}, + {FiletypeNewB, "NewB", "", "B", `DOS 3.3 Type "new B"`, "", false}, } var filetypeInfosMap map[Filetype]filetypeInfo var filetypeNames []string +var filetypeNamesExtras []string func init() { sosReserved := []Filetype{0x0D, 0x0E, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18} @@ -123,37 +125,46 @@ func init() { for _, typ := range sosReserved { info := filetypeInfo{ Type: typ, - Name: fmt.Sprintf("SOSReserved%02X", typ), + Name: fmt.Sprintf("SOSReserved%02X", int(typ)), ThreeLetter: "", OneLetter: "", - Desc: fmt.Sprintf("SOS reserved for future use %02X", typ), + Desc: fmt.Sprintf("SOS reserved for future use %02X", int(typ)), + Extra: true, } filetypeInfos = append(filetypeInfos, info) } for _, typ := range prodosReserved { info := filetypeInfo{ Type: typ, - Name: fmt.Sprintf("ProDOSReserved%02X", typ), + Name: fmt.Sprintf("ProDOSReserved%02X", int(typ)), ThreeLetter: "", OneLetter: "", - Desc: fmt.Sprintf("ProDOS reserved for future use %02X", typ), + Desc: fmt.Sprintf("ProDOS reserved for future use %02X", int(typ)), + Extra: true, } filetypeInfos = append(filetypeInfos, info) } + seen := map[string]bool{} + filetypeInfosMap = make(map[Filetype]filetypeInfo, len(filetypeInfos)) for i, info := range filetypeInfos { info.Stringified = info.Desc + " (" + info.Name - if info.ThreeLetter != "" { + if info.ThreeLetter != "" && !seen[info.ThreeLetter] { info.Stringified += "|" + info.ThreeLetter + seen[info.ThreeLetter] = true } - if info.OneLetter != "" { + if info.OneLetter != "" && info.OneLetter != info.Name && !seen[info.OneLetter] { info.Stringified += "|" + info.OneLetter + seen[info.OneLetter] = true } info.Stringified += ")" filetypeInfos[i] = info filetypeInfosMap[info.Type] = info - filetypeNames = append(filetypeNames, info.Stringified) + filetypeNamesExtras = append(filetypeNamesExtras, info.Stringified) + if !info.Extra { + filetypeNames = append(filetypeNames, info.Stringified) + } } } @@ -161,7 +172,7 @@ func (f Filetype) String() string { if info, found := filetypeInfosMap[f]; found { return info.Stringified } - return fmt.Sprintf("Invalid/unknown filetype %02X", f) + return fmt.Sprintf("Invalid/unknown filetype %02X", int(f)) } // FiletypeForName returns the filetype for a full, three-letter, or @@ -176,6 +187,9 @@ func FiletypeForName(name string) (Filetype, error) { } // FiletypeNames returns a list of all filetype names. -func FiletypeNames() []string { +func FiletypeNames(all bool) []string { + if all { + return filetypeNamesExtras + } return filetypeNames } diff --git a/lib/disk/ops.go b/lib/disk/ops.go index 180a0cf..d4860c2 100644 --- a/lib/disk/ops.go +++ b/lib/disk/ops.go @@ -40,7 +40,7 @@ type Operator interface { // 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. - PutFile(filename string, fileInfo FileInfo, overwrite bool) (existed bool, err error) + PutFile(fileInfo FileInfo, overwrite bool) (existed bool, err error) } // FileInfo represents a file descriptor plus the content. diff --git a/lib/dos3/dos3.go b/lib/dos3/dos3.go index 0dd2f33..3c311e9 100644 --- a/lib/dos3/dos3.go +++ b/lib/dos3/dos3.go @@ -374,9 +374,9 @@ func (fd FileDesc) descriptor() disk.Descriptor { case FiletypeRelocatable: // RELOCATABLE object module file desc.Type = disk.FiletypeRelocatable case FiletypeA: // A type file - desc.Type = disk.FiletypeA + desc.Type = disk.FiletypeNewA case FiletypeB: // B type file - desc.Type = disk.FiletypeB + desc.Type = disk.FiletypeNewB } return desc } @@ -660,7 +660,7 @@ 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(filename string, fileInfo disk.FileInfo, overwrite bool) (existed bool, err error) { +func (o operator) PutFile(fileInfo disk.FileInfo, overwrite bool) (existed bool, err error) { return false, fmt.Errorf("%s does not implement PutFile yet", operatorName) } diff --git a/lib/supermon/supermon.go b/lib/supermon/supermon.go index af26f1e..0fe26fc 100644 --- a/lib/supermon/supermon.go +++ b/lib/supermon/supermon.go @@ -662,7 +662,7 @@ 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(filename string, fileInfo disk.FileInfo, overwrite bool) (existed bool, err error) { +func (o operator) PutFile(fileInfo disk.FileInfo, overwrite bool) (existed bool, err error) { if fileInfo.Descriptor.Type != disk.FiletypeBinary { return false, fmt.Errorf("%s: only binary file type supported", operatorName) } @@ -670,7 +670,7 @@ func (o operator) PutFile(filename string, fileInfo disk.FileInfo, overwrite boo return false, fmt.Errorf("mismatch between FileInfo.Descriptor.Length (%d) and actual length of FileInfo.Data field (%d)", fileInfo.Descriptor.Length, len(fileInfo.Data)) } - numFile, namedFile, symbol, err := o.st.FilesForCompoundName(filename) + numFile, namedFile, symbol, err := o.st.FilesForCompoundName(fileInfo.Descriptor.Name) if err != nil { return false, err }