Video Coming Soon...
32: od
I believe I'd never used od until I wrote this exercise. That's one of the great things about these projects. You'll learn about tools you may never consider while you're learning about the Go standard library.
The od tool is a way to view the contents of a binary file as a sequence of octal values. Think of it as "cat for hackers." It's not the greatest way to view a binary file, and most people can't even read its output, but it is fun to implement.
The Challenge
You'll be required to implement the arguments -w, -x, and -o for the command line tool od. The instructions for od are:
You should try to use each option to understand what they do.
Requirements
While working on od I used the following libraries. You don't have to use these, but if you aren't sure where to start, these libraries are most likely a good place:
See the list of requirements
- bufio -- https://pkg.go.dev/bufio
- flag -- https://pkg.go.dev/flag
- fmt -- https://pkg.go.dev/fmt
- io -- https://pkg.go.dev/io
- log -- https://pkg.go.dev/log
- os -- https://pkg.go.dev/os
Spoilers
If you are totally stuck and can't figure it out, then you can view my code. If you're really, really stuck then take the time to do a copy of my code.
See my first version code
View Source file go-coreutils/od/main.go Onlypackage main
import (
"fmt"
"os"
"flag"
"log"
"bufio"
)
func Fail(err error, format string, v ...any) {
err_format := fmt.Sprintf("ERROR: %v; %s", err, format)
log.Printf(err_format, v...)
os.Exit(1)
}
type Opts struct {
Width int
Filenames []string
Hex bool
Octal bool
Format string
}
func parse_opts() (Opts) {
var opts Opts
flag.IntVar(&opts.Width, "w", 16, "Width of output grid")
flag.BoolVar(&opts.Hex, "x", false, "Output hex bytes")
flag.BoolVar(&opts.Octal, "o", false, "Output octal bytes")
flag.Parse()
if flag.NArg() == 0 {
log.Fatal("USAGE: od [files]")
}
if opts.Hex {
opts.Format = "%0.2x "
} else {
opts.Format = "%0.3o "
}
opts.Filenames = flag.Args()
return opts
}
func main() {
opts := parse_opts()
for _, filename := range opts.Filenames {
reader, err := os.Open(filename)
defer reader.Close()
if err != nil { Fail(err, "can't open: %s", filename) }
buf := bufio.NewReader(reader)
count := buf.Size()
fmt.Printf("%0.8o ", 0);
for index := 0; index < count; index++ {
data, err := buf.ReadByte()
if err != nil { break }
fmt.Printf(opts.Format, data);
if (index + 1) % opts.Width == 0 {
fmt.Print("\n")
fmt.Printf("%0.8o ", index);
}
}
}
}
If you do a copy, then follow the Remake It process. Get your copy working, take notes on how it worked, then delete it and do a version based on your notes.
Testing It
If you want to push your learning further then you can try to implement an automated test for this. I actually need to learn how to test utilities like this with Go, so for now just consider this an extra challenge for later until I learn how to teach it.
Register for Learn Go the Hard Way
Register today for the course and get the all currently available videos and lessons, plus all future modules for no extra charge.